@objc 与Swift 中运行时机制

391 阅读3分钟

在 Swift 中,@objc 关键字和运行时(Runtime)机制是两个重要的概念,分别服务于与 Objective-C 的互操作性和动态特性。以下是详细解析:


一、@objc 的作用

@objc 主要用于 桥接 Swift 代码与 Objective-C,使其能够被 Objective-C 调用或与 Objective-C 的动态特性兼容。具体作用包括:

  1. 暴露 Swift 类型到 Objective-C

    • :若 Swift 类需被 Objective-C 调用,必须显式继承 NSObject 或标记为 @objc。若类名包含非 ASCII 字符(如中文),需通过 @objc(ClassName) 指定 Objective-C 兼容的 ASCII 名称1610。

      swift

      @objc(MyClass)
      class 我的类: NSObject {
          @objc(greeting:)
          func 打招呼(名字: String) { ... }
      }
      
    • 协议:若协议包含 optional 方法,必须标记为 @objc,以支持 Objective-C 的可选方法特性110。

      swift

      @objc protocol CounterDataSource {
          optional func increment() -> Int
      }
      
    • 枚举:仅原始值为整型的非泛型枚举可被 @objc 修饰,以便在 Objective-C 中使用110。

  2. 重命名 Objective-C 符号
    可通过 @objc(Name) 自定义方法或属性在 Objective-C 中的名称,例如解决命名冲突或适配命名规范610。

  3. 动态特性支持

    • 标记为 @objc 的成员可通过 Objective-C 运行时动态调用(如 performSelector:)。
    • 但 @objc 不强制使用动态派发,若需完全动态行为(如 KVO、方法替换),需结合 dynamic 关键字67。

二、Swift 中的运行时机制

Swift 默认偏向静态派发以优化性能,但在特定场景下支持运行时动态特性:

  1. 动态派发

    • 函数表派发(vtable) :类的非 final 方法通过虚函数表实现动态派发,支持多态27。
    • 消息派发:使用 @objc 或 dynamic 修饰的方法,或继承自 NSObject 的类,可通过 Objective-C 的 objc_msgSend 实现消息派发67。
  2. 运行时反射与动态加载

    • 反射:通过 Mirror 类型可获取类型元数据(如属性、方法),但功能有限,不推荐高性能场景使用2。
    • 动态加载:使用 dlopen 和 dlsym 动态加载库,或通过 Objective-C 运行时库(如 objc_getClassList)遍历类信息5。
  3. 内存管理与 ARC
    Swift 运行时通过自动引用计数(ARC)管理内存,处理对象的生命周期及引用关系(强/弱/无主引用)2。

  4. 动态特性示例

    swift

    class MyClass: NSObject {
        @objc dynamic func foo() { ... } // 支持 KVO 和方法替换
    }
    
    • dynamic 强制方法使用消息派发,支持运行时干预(如 Method Swizzling)79。

三、使用场景对比

场景@objc 的作用运行时机制的应用
与 Objective-C 互调暴露 Swift 类、方法、属性到 Objective-C通过消息派发实现动态调用
实现可选协议方法必须标记协议为 @objc使用 Objective-C 的运行时特性
KVO 或方法替换结合 dynamic 使用依赖 Objective-C 的消息派发机制
减少包大小优化避免不必要的 @objc 标记静态派发减少运行时开销9

四、注意事项

  1. 性能权衡:动态派发(如消息机制)比静态派发慢,需在灵活性与性能间平衡27。
  2. ABI 稳定性:自 Swift 5 起,ABI 稳定,Swift 运行时库集成到操作系统中,减少了应用体积8。
  3. 混编限制:Swift 值类型(如结构体、枚举)无法直接暴露给 Objective-C,需通过 @objc 类封装68。

总结

  • @objc 是 Swift 与 Objective-C 互操作的桥梁,用于暴露符号、重命名及支持动态特性。
  • Swift 运行时 支持动态派发、反射和内存管理,但需通过 @objc 或 dynamic 显式启用动态行为。
  • 合理使用二者,可在保持 Swift 类型安全与性能的同时,灵活兼容 Objective-C 生态。