Swift多平台项目封装iOS专属可复用函数编译报错如何解决
问题原因
当前写法失效的核心逻辑:
函数内部的#if os(iOS)仅控制函数体内是否执行传入的闭包,但传入的闭包代码默认会在所有目标平台参与编译和类型检查。
非内联函数对调用侧编译器属于黑盒,编译器看不到函数内部的条件编译逻辑,必须保证传入的闭包在当前编译平台(比如macOS)类型合法。macOS SDK中不存在UIColor类型,哪怕闭包在macOS下永远不会执行,编译器依然会抛出类型找不到的错误。
正确实现方案
给函数添加@inlinable标记即可,该标记会让函数体在调用侧内联展开,条件编译判断会在调用点所属的平台上下文生效:
@inlinable func runOniOS(action: () -> Void) { #if os(iOS) action() #endif }
封装完成后原有调用代码不需要做任何修改,即可正常编译运行。
原理说明
- 添加
@inlinable后,函数源码会暴露给调用侧,编译器编译调用代码时,可以直接识别函数内部的条件编译逻辑 - 编译macOS目标时,编译器判定
os(iOS)条件不成立,action()调用会被直接裁剪,传入的闭包因为不存在执行路径,会被编译器优化移除,不会对闭包内的iOS专属代码做类型检查 - 编译iOS目标时,逻辑和原生条件编译完全一致,会正常执行传入的闭包,没有额外性能损耗
注意:不要使用「分平台定义函数、非iOS平台提供空实现」的写法,这种写法下非iOS平台的函数依然要求传入的闭包符合当前平台的类型规则,无法解决编译报错。
扩展说明
- 该方案兼容Swift 4.2及以上版本,覆盖绝大多数现役Xcode版本,不需要在调用点重复编写条件编译指令,完全符合复用需求
- 如果需要将该函数作为公共API暴露给其他模块使用,可以额外添加
@_alwaysEmitIntoClient标记,保证跨模块调用时逻辑正常生效 - Swift 5.9+版本也可以通过自定义独立宏实现相同效果,但
@inlinable方案实现成本更低,不需要额外编写宏定义
内容的提问来源于stack exchange,提问作者swiftPunk
相关产品推荐
相关产品推荐

