ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

SE-0274 简明魔数文件名(Concise magic file names):Swift 中 `file`、`filePath` 与 `fileID` 的设计与演进

SE-0274 简明魔数文件名(Concise magic file names):Swift 中 `file`、`filePath` 与 `fileID` 的设计与演进 SE-0274 简明魔数文件名Concise magic file namesSwift 中#file、#filePath与#fileID的设计与演进【免费下载链接】swift-evolutionThis maintains proposals for changes and user-visible enhancements to the Swift Programming Language.项目地址: https://gitcode.com/gh_mirrors/sw/swift-evolution导读本文以 Swift Evolution 提案 SE-0274「Concise magic file names」 为核心系统讲解 Swift 魔术标识符#file的语义变更、新的#filePath/#fileID表达式、字符串格式规范、#sourceLocation交互及默认参数失配诊断。读完本文你将理解为何 Swift 抛弃完整路径作为#file的默认值掌握ModuleName/FileName.swift格式字符串的解析规则并能在实际项目中正确选择#file、#filePath与#fileID同时了解ConciseMagicFile这一 upcoming feature flag 的启用方式与迁移路径。背景与动机为什么完整路径不是好默认值在 Swift 中魔术标识符#file原本求值为一个字符串字面量内容是编译时传给swiftc的源文件完整路径。它是定位日志、断言和错误信息出处的好帮手但完整路径带来了一系列问题见 SE-0274 的 Motivation 一节隐私泄露完整路径可能包含开发者用户名、构建农场配置、私有版本标识、外置磁盘命名等信息。而#file最常见的用法是作为默认参数如fatalError(_:file:line:)这使得信息泄露在使用点不可见——开发者根本不知道自己把什么写进了二进制。二进制膨胀Swift 基准测试显示更短的#file字符串最多可将代码体积减小约 5%同一批测试中数十个基准运行明显加快个别耗时降低约 22%。破坏可复现构建同一份代码在不同机器上构建会因路径不同而产生不同哈希的二进制妨碍构建产物缓存与二进制差异分析。同时人们期待从完整路径获得的收益其实并不存在路径在不同机器间不可移植除非本机目录布局与构建机完全一致否则无法用错误信息自动匹配到源码行。路径不保证是绝对路径——XCBuild 和 SwiftPM 恰好使用绝对路径但 Bazel 等构建系统并不会。路径不保证在进程内唯一——同一文件可能被多个模块包含不同项目可能在不同机器或不同时间位于相同路径。因此提案决定#file改而求值为人类可读、包含模块名与文件名的简短字符串同时新增#filePath保留旧行为。标准库的断言与错误函数assert、precondition、fatalError继续使用#file。核心方案#file语义变更与#filePath的引入提案核心是把#file的字符串格式改为module-name/file-name。以一个位于/Users/becca/Desktop/0274-magic-file.swift、模块名为MagicFile的文件为例SE-0274 示例print(#file) print(#filePath) fatalError(Something bad happened!)输出为MagicFile/0274-magic-file.swift /Users/becca/Desktop/0274-magic-file.swift Fatal error: Something bad happened!: file MagicFile/0274-magic-file.swift, line 3#file与#filePath的其他行为与旧的#file完全一致包括在默认参数中使用时捕获调用点位置call-site location。设计上的一个重要依据是Swift 编译器当前不允许两个同名文件无论路径如何编译进同一模块脚注 [1] 说明这是为了保证不同文件中private/fileprivate声明的混淆名唯一因此module-name/file-name在占用空间远小于完整路径的同时反而能更好地唯一标识文件也更便于人类阅读和工具反查。后续演进SE-0285 与#fileID需要特别说明的是#file行为变更经历了二次修订。SE-0285「Ease the transition to concise magic file strings」 修改了 SE-0274 的计划除#filePath外新增#fileID魔术标识符在所有语言模式下都生成简短的模块名/文件名字符串#file在 Swift 4、4.2、5 语言模式下继续生成与#filePath相同的字符串仅在未来的语言模式如 Swift 6中改为生成与#fileID相同的字符串届时#fileID被弃用标准库断言函数从#file切换到#fileID编译器生成的 trap如强制解包中的文件名也改为等价于#fileID的字面量。这一调整源于 SwiftNIO 等源码分发库的迁移困境它们需要借助#if compiler(5.3)兼容多版本却发现#file/#filePath在默认参数中的调用点魔法行为不具传递性——通过辅助函数抽象两者选择会捕获到辅助函数定义处的文件而#if又只能包裹完整语句或声明无法在默认参数内部使用详见 SE-0285 中的反例。SE-0285 还向 Swift API 设计指南的 Parameters 一节补充了建议生产环境运行的 API 优先使用#fileID仅在测试辅助与脚本等不会交付给最终用户的场景下若完整路径有助于开发流程或用于文件 I/O才使用#filePath#file仅用于兼容 Swift 5.2 及更早代码。#file字符串格式规范SE-0274 将#file生成的字符串格式形式化为如下文法规范原文file-string → module-name / file-name file-string → module-name / disambiguator / file-name // 预留扩展 module-name → [与 Swift 标识符相同] disambiguator → [除 U0000 NULL 外的任意字符] file-name → [除 U002F SOLIDUS 与 U0000 NULL 外的任意字符]关键约定文法中的/始终是字面量斜杠即使宿主系统使用不同的路径分隔符如 Windows 的\也不变disambiguator字段可以包含/字符因此解析时不能假设字符串恰好有三段正确的解析方式是按/切分后取第一个元素作为模块名、取最后一个元素作为文件名或者用第一个/的索引作为模块名结束位置、最后一个/之后的索引作为文件名起始位置。各组成部分的计算规则计算说明module-name正在编译的模块名file-name#filePath字符串中最后一个路径分隔符之后的子串路径分隔符指/或宿主系统分隔符Windows 上为\disambiguator当前编译器始终省略未来编译器可借助模块内其他潜在#filePath计算该字段以区分会产生相同file-string的多个路径。与#sourceLocation的交互#sourceLocation指令的file参数将指定#filePath的内容。由于file-string由#filePath计算而来这意味着file参数的最后一段会被用作file-name。提案明确不提供修改module-name或直接指定file-string的途径交互说明。当前编译器中#sourceLocation引入的路径可能与其他#sourceLocation路径或物理文件产生#file字符串冲突编译器会对此发出警告未来编译器可能改用disambiguator字段来区分这些冲突。配套地SourceKit 将提供把#file字符串映射回对应#filePath的工具设施Tooling 一节。默认参数失配诊断包装函数的守护#file与#filePath最常见的用途是作为函数默认参数。当一个包装函数把捕获了某一魔术标识符的参数传递给另一使用不同魔术标识符默认参数的函数时会产生难以察觉的行为差异。为此编译器会发出警告当某个捕获了魔术标识符的参数被传给默认参数捕获了不同魔术标识符的函数时触发诊断该警告同样适用于既有组合如#file对#function、#line对#column诊断说明。提案未指定精确的警告文本但给出了示意func fn1(file: String #filePath) { ... } func fn2(file: String #file) { fn1(file: file) }可能的诊断形如sample.swift:3: warning: parameter file with default argument #file passed to parameter file, whose default argument is #filePath fn1(file: file) ^~~~ sample.swift:2: note: did you mean for parameter file to default to #filePath? func fn2(file: String #file) { ^~~~~ #filePath sample.swift:3: note: add parentheses to silence this warning fn1(file: file) ^ ^ ( )SE-0285 对警告规则做了细化参数默认值在#file与#fileID之间互相传递不警告在 Swift 5 及更早模式下#file与#filePath互相传递也不警告——因为#file在 Swift 5 模式下本就等价于#filePath。虽然#file替换#fileID理论上会有行为差异、理想情况应警告但为了避免给现有包装#fileID函数的代码带来新警告编译器选择不在此场景告警毕竟实践中这类#file只是生成了更大的字符串而已SE-0285 相关章节。实现状态与ConciseMagicFileupcoming feature flagSE-0274 的状态为Implemented (Swift 5.8)关联的 upcoming feature flag 为ConciseMagicFile见提案头。提案撰写时原型已进入 master可通过-Xfrontend -enable-experimental-concise-pound-file启用原型当时使用file-name (module-name)格式且不含#sourceLocation冲突警告与工具支持。经过 SE-0285 的修订后语义变更被推迟到下一个主语言版本Swift 6并可通过 SE-0362「Piecemeal adoption of upcoming language improvements」 引入的upcoming feature flag 机制提前启用swiftc -enable-upcoming-feature ConciseMagicFile main.swift启用ConciseMagicFile后#file在 Swift 5 模式下的含义从#filePath变为#fileIDSE-0362 中的说明。SE-0362 还规定某个 feature 在更高语言版本中默认启用后再显式传-enable-upcoming-feature X会产生错误未识别的 feature 名会被编译器忽略以便旧工具沿用相同命令行。SwiftPM 侧可通过SwiftSetting.enableUpcomingFeature(_:_:)在包清单中声明所需 feature其效果不跨模块边界SwiftPM 支持。此外SE-0486「Adoption tooling for Swift features」 将ConciseMagicFile列为具备机械化迁移路径的 upcoming feature 之一其迁移方式即#file→#filePath替换为显式完整路径以保持行为不变。启用此类 feature 的迁移模式migration mode时编译器不会引入新的错误或行为变化而是发出附带 fix-it 的警告以帮助保持兼容SE-0486 的 Automation 一节。兼容性影响源码兼容性所有现有源码在此变更下仍可编译——#file的文档从未精确定义其内容Swift 社区认为这足以满足源码兼容要求。但该变更确实会改变既有代码的运行时行为受影响代码改用#filePath即可恢复旧行为。#file行为变更被推迟至下一个主语言版本并可通过ConciseMagicFileflag 提前启用兼容性章节。ABI 稳定性无影响。#file是编译期特性既有二进制照常运行。API resilience与任何语法新增一样旧编译器无法使用包含#filePath默认参数或内联代码的模块接口文件swiftinterface。此外 SE-0285 补充了跨语言模式库的交互规则Swift 5 模式客户端使用新版库时库中#file默认参数按#fileID处理反之未来语言模式客户端使用 Swift 5 库时库中#file默认参数按#filePath处理SE-0285。备选方案回顾为什么不做这些设计提案在 Alternatives considered 一节记录了大量被否决的设计理解这些取舍有助于把握最终方案备选方案全文弃用#file并新增#fileName更保守但#fileName名不副实字符串实际还含模块名且迫使开发者逐一迁移所有#file用法负担沉重还给不出使用指引。团队最终认为#file占据简短名字、必要时才用#filePath是更温和的引导。支持超过两种#file变体曾考虑编译器 flag 或多种魔术标识符组合出 6 种行为编译调用中的路径、保证绝对路径、相对SOURCE_DIR的路径、纯文件名、文件名模块名、空串。结论是#filePath路径与#file文件名模块名两种足以覆盖用例5 种语法会浪费语言表面积编译器 flag 则制造语言方言。让#filePath恒为绝对路径更稳定但破坏分布式构建除非尊重-debug-prefix-map且无法简单复现 Swift 原有精确行为应作为独立提案另行讨论。单独提供#moduleName与#fileName#fileName单独使用歧义太大fatalError(_:file:line:)这类既有 API 只有一个文件名参数加第二个参数会破坏 ABI且默认参数中无法用字符串插值拼接两个魔术标识符插值会捕获被调用方位置而非调用点。但提案认可#moduleName有一定价值可从#file字符串中解析出来必要时可另行提案。保持file-string格式不指定一审反馈表明客户端需要解释该字符串因此必须指定格式含预留的disambiguator字段。引入#context等综合上下文标识符跨优化边界如模块边界传递时必须保守地生成调用方可能需要的全部信息无法获得隐私与体积收益。以编译器 flag 切换新旧行为制造语言方言且编译器 flag 不是用户的自然接口。不提供逃生通道地直接改行为旧行为在少数场景仍有价值不应彻底移除。实践建议小结结合 SE-0274、SE-0285 与 SE-0362 的内容在实际项目中可以这样决策场景推荐用法生产环境运行的 API日志、断言、错误上报#fileIDSwift 5.3 即刻可用字符串最精简测试辅助、脚本等需要完整路径做文件 I/O 的场景#filePath需要兼容 Swift 5.2 及更早的库代码#fileSwift 5 模式下仍等价于#filePath想在 Swift 5 模式提前体验#file新语义-enable-upcoming-feature ConciseMagicFile解析#file/#fileID字符串按/切分取首段为模块名、末段为文件名勿假定段数需要特别提醒的是不要把#file/#filePath塞进辅助函数来抽象选择魔术标识符的调用点捕获行为不具传递性那样拿到的将是辅助函数定义处的文件SE-0285 警告示例。延伸阅读过渡方案SE-0285 Ease the transition to concise magic file strings启用机制SE-0362 Piecemeal adoption of upcoming language improvements迁移工具SE-0486 Adoption tooling for Swift features相关演进SE-0028 Modernizing debug identifiers、swift-testing 提案集中对#filePath语义的后续讨论如 proposals/testing/0020-sourcelocation-filepath.md【免费下载链接】swift-evolutionThis maintains proposals for changes and user-visible enhancements to the Swift Programming Language.项目地址: https://gitcode.com/gh_mirrors/sw/swift-evolution创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表