为什么 Swift 3 @objc 推断正在成为过去?
\n\n如果你曾经将项目从 Swift 3 更新到 Swift 4,那么你很可能遇到过这个令人不安的编译器警告:
\n\nThe use of Swift 3 @objc inference in Swift 4 mode is deprecated. \nPlease address this deprecated @objc inference warning, \nor disable the warning by setting the "Swift 3 @objc Inference" \nbuild setting to "Default" for the "Swift 4" target.\n\n这条消息并非只是形式。它背后是语言哲学的一个重要变化。在 Swift 3 中,编译器自行决定哪些方法和属性需要对 Objective-C 可见。这很方便,但也带来了隐藏的问题:二进制体积增大,性能下降。Swift 4 决定终结这种“猜测”,并将控制权交还给开发者。
\n\n在本文中,我们将探讨什么是 @objc 推断,为什么它被 弃用,如何修复该警告,以及在现代项目中应使用哪些最佳实践。我们开始吧!
\n\n什么是 @objc 推断,它在 Swift 3 中是如何工作的?
\n\n在 Swift 3 中,存在隐式的 @objc 转换。这意味着,如果一个类继承自 NSObject 或使用了 @objc 协议,编译器会自动使其所有方法对 Objective-C 可见。这种机制被称为 @objc 推断(隐式推断)。
\n\nSwift 3 中的示例:
\n\nclass MyViewController: UIViewController {\n func buttonTapped() {\n print("Button tapped")\n }\n}\n\n在 Swift 3 中,方法 buttonTapped() 会自动变为 @objc。你可以将其传递给 #selector,或将其用作 UIControl 的 target。方便吗?是的。但代价是二进制文件的不必要膨胀和优化机会的丧失。
为什么 Apple 决定放弃自动推断?
\n\n原因有几个,都关乎效率和可控性:
\n\n- \n
- 应用程序大小。 每个标记为 @objc 的方法都会为 Objective-C 运行时生成额外的包装代码。在大型项目中,这可能会增加数兆字节的多余代码。 \n
- 性能。 Swift 是一种具有静态类型和强大优化能力的语言。一旦方法变为 @objc,编译器就会失去应用一系列优化的能力(例如内联)。 \n
- 明确性。 Swift 4 走上了明确性的路线。开发者应自行决定哪些内容应对 Objective-C 可见。这简化了代码理解,并减少了意外错误。 \n
- Swift 的未来。 Apple 正在逐步摆脱对 Objective-C 的依赖。隐式桥接越少,Swift 就能越快成为完全独立的语言。 \n
警告在实践中是什么样子的?
\n\n假设你有一个用 Swift 3 编写的项目。你将 "Swift Language Version" 设置切换为 Swift 4.0,然后收到数十个(甚至数百个)这样的警告:
\n\nclass LegacyViewController: UIViewController {\n // 警告:在 Swift 4 模式下使用 Swift 3 @objc 推断已弃用\n func legacyAction() {\n // ...\n }\n\n @objc func explicitAction() {\n // 此方法已标记 @objc — 一切正常\n }\n}\n\n编译器对 legacyAction() 报错,因为它没有标记 @objc,但以前编译器会自动执行此操作。现在需要解决方案。
修复警告的三种方法
\n\n你有三种选择。选择最适合你项目的那一种: