前言
Swift 和 Objective-C 的混编是 iOS 开发中的常见场景。无论是老项目引入 Swift,还是 Swift 项目使用 OC 第三方库,都需要两种语言之间的互操作。
本文将深入探讨 Swift 与 OC 混编的底层实现原理,包括:桥接机制、内存模型差异、方法调度、类型转换等核心内容。
一、混编基础架构
1.1 混编的两种方向
┌─────────────────────────────────────────────────────────────────────┐
│ Swift 与 OC 混编方向 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 【方向 1: OC 调用 Swift】 │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Objective-C │ │ Swift │ │
│ │ │ │ │ │
│ │ #import │ │ @objc │ │
│ │ "ModuleName- │ ──────> │ class MyClass │ │
│ │ Swift.h" │ │ │ │
│ │ │ │ @objc │ │
│ │ [obj method] │ │ func method() │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ════════════════════════════════════════════════════════════════ │
│ │
│ 【方向 2: Swift 调用 OC】 │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Swift │ │ Objective-C │ │
│ │ │ │ │ │
│ │ import Module │ │ @interface │ │
│ │ 或 │ ──────> │ MyClass │ │
│ │ 桥接头文件 │ │ │ │
│ │ │ │ - (void)method │ │
│ │ obj.method() │ │ │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ 关键文件: │
│ • xxx-Bridging-Header.h : Swift 调用 OC 的桥接头文件 │
│ • xxx-Swift.h : OC 调用 Swift 的自动生成头文件 │
│ │
└─────────────────────────────────────────────────────────────────────┘
1.2 桥接头文件机制
┌─────────────────────────────────────────────────────────────────────┐
│ 桥接头文件工作原理 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 【Bridging Header - Swift 访问 OC】 │
│ │
│ MyApp-Bridging-Header.h │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ // 导入需要在 Swift 中使用的 OC 头文件 │ │
│ │ #import "MyOCClass.h" │ │
│ │ #import "LegacyHelper.h" │ │
│ │ #import <SomeFramework/SomeFramework.h> │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Swift 编译器 (Clang Importer) │ │
│ │ │ │
│ │ 1. 解析 OC 头文件 │ │
│ │ 2. 将 OC 接口转换为 Swift 接口 │ │
│ │ 3. 生成 Swift 可用的模块声明 │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Swift 代码中可以直接使用 │ │
│ │ │ │
│ │ let obj = MyOCClass() │ │
│ │ obj.someMethod() │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ════════════════════════════════════════════════════════════════ │
│ │
│ 【Generated Header - OC 访问 Swift】 │
│ │
│ Swift 源码 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ @objc public class NetworkManager: NSObject { │ │
│ │ @objc public func fetchData() { } │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ 编译时自动生成 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ MyApp-Swift.h (自动生成,不要手动编辑) │ │
│ │ │ │
│ │ SWIFT_CLASS("_TtC5MyApp14NetworkManager") │ │
│ │ @interface NetworkManager : NSObject │ │
│ │ - (void)fetchData; │ │
│ │ - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; │ │
│ │ @end │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ OC 代码中使用 │ │
│ │ │ │
│ │ #import "MyApp-Swift.h" │ │
│ │ │ │
│ │ NetworkManager *manager = [[NetworkManager alloc] init]; │ │
│ │ [manager fetchData]; │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
1.3 模块化与混编
swift
// Framework 中的混编更复杂
// 需要使用 umbrella header 和 module.modulemap
// MyFramework.h (Umbrella Header)
#import <MyFramework/PublicOCClass.h>
#import <MyFramework/AnotherOCClass.h>
// module.modulemap
framework module MyFramework {
umbrella header "MyFramework.h"
export *
module * { export * }
}
// Swift 类需要 @objc 和 public
@objc public class SwiftClass: NSObject {
@objc public func publicMethod() { }
}
二、@objc 与 @objcMembers 深度解析
2.1 @objc 的作用
swift
// @objc 将 Swift 声明暴露给 Objective-C 运行时
// 1. 基本用法
@objc class MyClass: NSObject {
@objc var name: String = ""
@objc func doSomething() { }
}
// 2. 自定义 OC 中的名称
@objc(MYCustomClass)
class MyClass: NSObject {
@objc(customName)
var name: String = ""
@objc(doSomethingWithParam:)
func doSomething(param: String) { }
}
// 在 OC 中:
// MYCustomClass *obj = [[MYCustomClass alloc] init];
// obj.customName = @"test";
// [obj doSomethingWithParam:@"value"];
2.2 @objc 的底层实现
┌─────────────────────────────────────────────────────────────────────┐
│ @objc 底层实现 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Swift 类定义: │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ @objc class Person: NSObject { │ │
│ │ @objc var age: Int = 0 │ │
│ │ @objc func sayHello() { print("Hello") } │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ 编译后生成 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ObjC 类结构 (class_ro_t) │ │
│ │ │ │
│ │ 类名: _TtC8MyModule6Person (Swift mangled name) │ │
│ │ │ │
│ │ 方法列表 (method_list_t): │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ SEL: age │ IMP: swift_getProperty_impl │ │ │
│ │ │ SEL: setAge: │ IMP: swift_setProperty_impl │ │ │
│ │ │ SEL: sayHello │ IMP: _$s8MyModule6PersonC8sayHello │ │ │
│ │ │ SEL: init │ IMP: _$s8MyModule6PersonCACycfc │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 属性列表 (property_list_t): │ │
│ │ ┌─────────────────────────────────────────────────────┐ │ │
│ │ │ name: age │ attributes: T@"NSNumber",&,N,Vage │ │ │
│ │ └─────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ @objc 做了什么: │
│ 1. 在 ObjC 运行时注册类 │
│ 2. 为方法生成 SEL 和 ObjC 兼容的 IMP │
│ 3. 生成属性的 getter/setter │
│ 4. 处理 Swift 类型到 ObjC 类型的桥接 │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.3 @objcMembers 批量暴露
swift
// @objcMembers 自动为所有成员添加 @objc
@objcMembers
class DataManager: NSObject {
var items: [String] = [] // 自动 @objc
func addItem(_ item: String) { } // 自动 @objc
func removeItem(at index: Int) { } // 自动 @objc
// 私有成员不会暴露
private func internalMethod() { }
// 不兼容 ObjC 的类型不会暴露
func processData(_ data: (Int, Int)) { } // 元组不兼容,不暴露
}
// 等价于:
class DataManager: NSObject {
@objc var items: [String] = []
@objc func addItem(_ item: String) { }
@objc func removeItem(at index: Int) { }
}
2.4 @objc 的限制
swift
// 以下类型不能用 @objc
// 1. 结构体
struct Point { // ❌ 不能 @objc
var x: Double
var y: Double
}
// 2. 枚举(除非是 Int 类型)
enum Status { // ❌ 不能 @objc
case active, inactive
}
@objc enum IntStatus: Int { // ✅ Int 枚举可以
case active = 0
case inactive = 1
}
// 3. 泛型
class Container<T> { // ❌ 泛型类不能 @objc
var value: T?
}
// 4. 元组
@objc func returnTuple() -> (Int, Int) { } // ❌ 不能
// 5. Swift 独有特性
@objc func withDefaultParam(value: Int = 0) { } // ❌ 默认参数不生效
@objc func variadic(_ values: Int...) { } // ❌ 可变参数不能
// 6. 嵌套类型
class Outer {
@objc class Inner: NSObject { } // ❌ 嵌套类型不能 @objc
}
三、方法调度机制对比
3.1 Swift 方法调度类型
┌─────────────────────────────────────────────────────────────────────┐
│ Swift 方法调度方式 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 【1. 静态调度 (Static Dispatch)】 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • 编译时确定调用地址 │ │
│ │ • 无运行时开销,可内联优化 │ │
│ │ │ │
│ │ 适用于: │ │
│ │ • 结构体/枚举的方法 │ │
│ │ • final 类的方法 │ │
│ │ • private/fileprivate 方法 │ │
│ │ • 类的 static 方法 │ │
│ │ │ │
│ │ struct Point { │ │
│ │ func distance() -> Double { ... } // 静态调度 │ │
│ │ } │ │
│ │ │ │
│ │ final class Manager { │ │
│ │ func process() { ... } // 静态调度 │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 【2. 虚表调度 (V-Table Dispatch)】 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • 类似 C++ 虚函数表 │ │
│ │ • 通过类的虚表查找方法地址 │ │
│ │ │ │
│ │ 适用于: │ │
│ │ • 类的普通实例方法(可被继承/重写) │ │
│ │ │ │
│ │ class Animal { │ │
│ │ func speak() { print("...") } // V-Table │ │
│ │ } │ │
│ │ class Dog: Animal { │ │
│ │ override func speak() { print("Woof") } │ │
│ │ } │ │
│ │ │ │
│ │ V-Table 结构: │ │
│ │ ┌────────────────────────────────────────────┐ │ │
│ │ │ Animal vtable: │ │ │
│ │ │ [0] speak -> Animal.speak │ │ │
│ │ ├────────────────────────────────────────────┤ │ │
│ │ │ Dog vtable: │ │ │
│ │ │ [0] speak -> Dog.speak (override) │ │ │
│ │ └────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 【3. 消息调度 (Message Dispatch)】 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • ObjC runtime 的 objc_msgSend │ │
│ │ • 最灵活但最慢 │ │
│ │ • 支持 Method Swizzling、KVO 等 │ │
│ │ │ │
│ │ 适用于: │ │
│ │ • 继承自 NSObject 的 @objc 方法 │ │
│ │ • @objc dynamic 方法 │ │
│ │ │ │
│ │ class ViewController: UIViewController { │ │
│ │ @objc dynamic func handleTap() { } // 消息调度 │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ 【4. Witness Table (协议)】 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • 用于协议类型的方法调度 │ │
│ │ │ │
│ │ protocol Drawable { │ │
│ │ func draw() │ │
│ │ } │ │
│ │ │ │
│ │ let items: [Drawable] = [Circle(), Square()] │ │
│ │ for item in items { │ │
│ │ item.draw() // 通过 witness table 调度 │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
3.2 @objc dynamic 的必要性
swift
// 场景:需要 ObjC 运行时特性
// 1. KVO 监听
class Person: NSObject {
// ❌ 普通 @objc 属性,KVO 可能不工作
@objc var name: String = ""
// ✅ @objc dynamic 确保使用消息调度,KVO 正常工作
@objc dynamic var age: Int = 0
}
// 使用
let person = Person()
person.addObserver(self, forKeyPath: "age", options: .new, context: nil)
person.age = 25 // 触发 KVO
// 2. Method Swizzling
class ViewController: UIViewController {
// ❌ V-Table 调度,不能 swizzle
override func viewDidLoad() { }
// ✅ 消息调度,可以 swizzle
@objc dynamic func customMethod() { }
}
// 3. 选择器
class Handler: NSObject {
// 需要 @objc 才能用 #selector
@objc func handleTap(_ sender: UIButton) { }
}
let button = UIButton()
button.addTarget(handler, action: #selector(Handler.handleTap(_:)), for: .touchUpInside)
3.3 调度方式性能对比
┌─────────────────────────────────────────────────────────────────────┐
│ 方法调度性能对比 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 调度方式 │ 相对速度 │ 说明 │
│ ───────────────┼───────────┼─────────────────────────────────── │
│ 静态调度 │ 最快 1x │ 编译时确定,可内联 │
│ V-Table │ 快 ~1.2x │ 一次间接跳转 │
│ Witness Table │ 中 ~2x │ 额外的 existential 开销 │
│ 消息调度 │ 慢 ~4x │ 运行时查找,缓存未命中更慢 │
│ │
│ 注意:这是理论值,实际差异在大多数场景中可忽略 │
│ 应根据功能需求选择,而不是过早优化 │
│ │
│ ════════════════════════════════════════════════════════════════ │
│ │
│ 性能敏感场景的建议: │
│ │
│ // 高频调用使用 final 或 struct │
│ final class FastProcessor { │
│ func process(_ data: [Int]) { ... } │
│ } │
│ │
│ struct MathHelper { │
│ func calculate(_ x: Double) -> Double { ... } │
│ } │
│ │
│ // 需要动态特性时才用 @objc dynamic │
│ class ViewModel: NSObject { │
│ @objc dynamic var data: [String] = [] // 需要 KVO │
│ func processData() { ... } // 不需要 @objc │
│ } │
│ │
└─────────────────────────────────────────────────────────────────────┘
四、类型桥接机制
4.1 自动桥接类型
┌─────────────────────────────────────────────────────────────────────┐
│ Swift - ObjC 类型桥接 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Swift 类型 │ ObjC 类型 │ 桥接方式 │
│ ───────────────────┼─────────────────────┼──────────────────── │
│ String │ NSString │ 零开销桥接 │
│ Array │ NSArray │ 零开销桥接 │
│ Dictionary │ NSDictionary │ 零开销桥接 │
│ Set │ NSSet │ 零开销桥接 │
│ Int, UInt, Float... │ NSNumber │ 装箱/拆箱 │
│ Bool │ BOOL / NSNumber │ 装箱/拆箱 │
│ Data │ NSData │ 零开销桥接 │
│ Date │ NSDate │ 零开销桥接 │
│ URL │ NSURL │ 零开销桥接 │
│ Error │ NSError │ 零开销桥接 │
│ AnyObject │ id │ 直接对应 │
│ Any │ id │ 装箱 │
│ │
└─────────────────────────────────────────────────────────────────────┘
4.2 零开销桥接原理
swift
// String 和 NSString 共享相同的内存布局
// 桥接时不需要复制数据
let swiftString: String = "Hello"
let nsString: NSString = swiftString as NSString // 无复制
// 底层实现
// String 内部可以存储:
// 1. 小字符串 (Small String) - 内联存储
// 2. Native Swift String - Swift 管理的堆内存
// 3. Bridged NSString - 直接引用 NSString 对象
// 当 as NSString 时:
// - 如果是 Native String,创建一个 NSString wrapper
// - 如果已经是 Bridged NSString,直接返回
// 验证零开销
import Foundation
func testBridging() {
let str: NSString = "Test"
let swiftStr = str as String
// 两者指向相同的存储
str.withCString { ptr1 in
swiftStr.withCString { ptr2 in
print("Same storage: \(ptr1 == ptr2)") // 可能为 true
}
}
}
4.3 装箱类型的开销
swift
// 值类型到 AnyObject/id 需要装箱
// 1. 数字类型装箱
let intValue: Int = 42
let nsNumber = intValue as NSNumber // 创建 NSNumber 对象
// 底层:
// NSNumber *number = [[NSNumber alloc] initWithLongLong:42];
// 2. Any 的装箱
func sendToOC(_ value: Any) {
// 如果是值类型,需要装箱
}
sendToOC(42) // Int 装箱为 NSNumber
sendToOC("test") // String 桥接为 NSString(零开销)
sendToOC(true) // Bool 装箱为 NSNumber
// 3. 避免不必要的装箱
// ❌ 低效
func processNumbers(_ numbers: [Any]) {
for n in numbers {
if let int = n as? Int {
// 每次需要拆箱
}
}
}
// ✅ 高效
func processNumbers(_ numbers: [Int]) {
for n in numbers {
// 直接使用,无装箱/拆箱
}
}
4.4 自定义类型桥接
swift
// 通过 _ObjectiveCBridgeable 协议实现自定义桥接
// ObjC 类
@interface LegacyPoint : NSObject
@property (nonatomic) double x;
@property (nonatomic) double y;
@end
// Swift 结构体
struct Point {
var x: Double
var y: Double
}
// 实现桥接
extension Point: _ObjectiveCBridgeable {
typealias _ObjectiveCType = LegacyPoint
func _bridgeToObjectiveC() -> LegacyPoint {
let obj = LegacyPoint()
obj.x = x
obj.y = y
return obj
}
static func _forceBridgeFromObjectiveC(_ source: LegacyPoint,
result: inout Point?) {
result = Point(x: source.x, y: source.y)
}
static func _conditionallyBridgeFromObjectiveC(_ source: LegacyPoint,
result: inout Point?) -> Bool {
result = Point(x: source.x, y: source.y)
return true
}
static func _unconditionallyBridgeFromObjectiveC(_ source: LegacyPoint?) -> Point {
guard let source = source else {
return Point(x: 0, y: 0)
}
return Point(x: source.x, y: source.y)
}
}
// 使用
let point = Point(x: 1.0, y: 2.0)
let legacyPoint = point as LegacyPoint // 自动桥接
let backToSwift = legacyPoint as Point // 自动桥接
五、内存管理互操作
5.1 ARC 在两种语言中的对应
┌─────────────────────────────────────────────────────────────────────┐
│ Swift 与 OC 内存管理对应 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Swift 所有权 │ OC 等价物 │
│ ───────────────────┼──────────────────────────────────────────── │
│ strong (默认) │ strong / retain │
│ weak │ weak │
│ unowned │ unsafe_unretained (但更安全) │
│ unowned(unsafe) │ unsafe_unretained │
│ │
│ ════════════════════════════════════════════════════════════════ │
│ │
│ 【属性修饰符对应】 │
│ │
│ Swift: ObjC: │
│ ┌─────────────────────────┐ ┌─────────────────────────────┐ │
│ │ var delegate: Delegate? │ │ @property (weak) id delegate│ │
│ │ weak var ... │ │ │ │
│ └─────────────────────────┘ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────────┐ │
│ │ var name: String │ │ @property (strong) NSString │ │
│ │ │ │ *name; │ │
│ └─────────────────────────┘ └─────────────────────────────┘ │
│ │
│ ┌─────────────────────────┐ ┌─────────────────────────────┐ │
│ │ unowned var parent: │ │ @property (unsafe_unretained)│ │
│ │ Parent │ │ Parent *parent; │ │
│ └─────────────────────────┘ └─────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
5.2 闭包/Block 的内存管理
swift
// Swift 闭包和 OC Block 的互操作
// 1. Swift 闭包传给 OC
class NetworkManager: NSObject {
@objc func request(completion: @escaping (Data?, Error?) -> Void) {
// Swift 闭包被转换为 OC Block
// @escaping 确保闭包可以在方法返回后调用
DispatchQueue.global().async {
let data = Data()
completion(data, nil)
}
}
}
// OC 端使用:
// [manager requestWithCompletion:^(NSData *data, NSError *error) {
// // ...
// }];
// 2. OC Block 在 Swift 中使用
// OC 定义:
// typedef void (^CompletionBlock)(BOOL success);
// - (void)performWithCompletion:(CompletionBlock)completion;
// Swift 使用:
let ocClass = SomeOCClass()
ocClass.perform { success in
print("Completed: \(success)")
}
// 3. 循环引用问题
class ViewController: UIViewController {
var networkManager = NetworkManager()
var data: Data?
func loadData() {
// ❌ 循环引用
networkManager.request { data, error in
self.data = data // self 强引用
}
// ✅ 使用 weak
networkManager.request { [weak self] data, error in
self?.data = data
}
// ✅ 或 unowned (确定 self 不会先被释放)
networkManager.request { [unowned self] data, error in
self.data = data
}
}
}
5.3 Unmanaged 类型
swift
// 处理手动内存管理的 Core Foundation 类型
// 1. 获取未管理引用
let unmanagedString = Unmanaged.passUnretained("Hello" as CFString)
let cfString = unmanagedString.takeUnretainedValue()
// 2. 转移所有权
func createCFString() -> Unmanaged<CFString> {
let str = "Hello" as CFString
return Unmanaged.passRetained(str) // +1 retain
}
let unmanaged = createCFString()
let str = unmanaged.takeRetainedValue() // 接管所有权,平衡 retain
// 3. 与 OC API 交互
// 某些 C/OC API 返回未管理的对象
let addressBook = ABAddressBookCreate()?.takeRetainedValue()
// 4. 使用 __bridge
// Swift 中等价于:
let cfStr = unsafeBitCast("Hello" as NSString, to: CFString.self)
六、协议互操作
6.1 @objc protocol
swift
// 可以被 OC 类实现的协议
@objc protocol DataSource: AnyObject {
// 必须实现
func numberOfItems() -> Int
func item(at index: Int) -> Any
// 可选方法
@objc optional func title() -> String?
@objc optional var headerView: UIView? { get }
}
// OC 实现:
// @interface MyDataSource : NSObject <DataSource>
// @end
//
// @implementation MyDataSource
// - (NSInteger)numberOfItems { return 10; }
// - (id)itemAtIndex:(NSInteger)index { return @"item"; }
// // 可选方法可以不实现
// @end
// Swift 使用:
class ViewController: UIViewController {
weak var dataSource: DataSource?
func loadData() {
guard let ds = dataSource else { return }
let count = ds.numberOfItems()
// 可选方法需要特殊调用
if let title = ds.title?() {
print(title)
}
// 或者检查是否响应
if ds.responds(to: #selector(DataSource.title)) {
// ...
}
}
}
6.2 Swift 协议 vs OC 协议
┌─────────────────────────────────────────────────────────────────────┐
│ Swift 协议 vs OC 协议 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 特性 │ Swift 协议 │ @objc 协议 │
│ ─────────────────┼───────────────────┼───────────────────────── │
│ 值类型实现 │ ✅ 支持 │ ❌ 不支持 │
│ 泛型/关联类型 │ ✅ 支持 │ ❌ 不支持 │
│ 静态方法要求 │ ✅ 支持 │ ✅ 支持 (+方法) │
│ 可选方法 │ ❌ 需要默认实现 │ ✅ @objc optional │
│ 运行时检查 │ ❌ 编译时 │ ✅ responds(to:) │
│ 方法调度 │ Witness Table │ 消息发送 │
│ OC 互操作 │ ❌ 不可见 │ ✅ 完全兼容 │
│ │
│ ════════════════════════════════════════════════════════════════ │
│ │
│ 【何时使用 @objc protocol】 │
│ │
│ 1. 需要 OC 代码实现 │
│ @objc protocol LegacyDelegate { │
│ func legacyCallback() │
│ } │
│ │
│ 2. 需要可选方法 │
│ @objc protocol DataSource { │
│ @objc optional func supplementaryData() -> Any? │
│ } │
│ │
│ 3. 需要运行时检查 │
│ if object.responds(to: #selector(SomeProtocol.optionalMethod)) { │
│ // ... │
│ } │
│ │
│ 【何时使用纯 Swift 协议】 │
│ │
│ 1. 纯 Swift 项目 │
│ protocol Identifiable { │
│ associatedtype ID: Hashable │
│ var id: ID { get } │
│ } │
│ │
│ 2. 需要泛型/关联类型 │
│ protocol Container { │
│ associatedtype Element │
│ var items: [Element] { get } │
│ } │
│ │
│ 3. 值类型需要实现 │
│ protocol Drawable { │
│ func draw() │
│ } │
│ struct Circle: Drawable { ... } // 结构体实现 │
│ │
└─────────────────────────────────────────────────────────────────────┘
6.3 协议扩展与 OC
swift
// 协议扩展的方法对 OC 不可见
protocol Greetable {
func greet() -> String
}
extension Greetable {
// 默认实现 - OC 不可见
func greet() -> String {
return "Hello"
}
// 扩展方法 - OC 不可见
func greetLoudly() -> String {
return greet().uppercased()
}
}
@objc protocol ObjCGreetable: Greetable {
// 必须声明在协议中的方法才对 OC 可见
@objc func greet() -> String
}
class Greeter: NSObject, ObjCGreetable {
// 必须提供实现
func greet() -> String {
return "Hi"
}
}
// OC 只能调用 greet,不能调用 greetLoudly
七、泛型与混编
7.1 泛型的限制
swift
// Swift 泛型不能直接暴露给 OC
// ❌ 不能 @objc
class Container<T> {
var items: [T] = []
}
// ❌ 泛型方法也不能
class Manager: NSObject {
@objc func process<T>(_ item: T) { } // 编译错误
}
// ✅ 变通方案1:使用类型擦除
class AnyContainer: NSObject {
private var items: [Any] = []
@objc func addItem(_ item: Any) {
items.append(item)
}
@objc func getItem(at index: Int) -> Any? {
guard index < items.count else { return nil }
return items[index]
}
}
// ✅ 变通方案2:使用协议
@objc protocol Processable {
func process()
}
class Manager: NSObject {
@objc func processItem(_ item: Processable) {
item.process()
}
}
// ✅ 变通方案3:具体类型子类
class StringContainer: NSObject {
var items: [String] = []
@objc func addItem(_ item: String) {
items.append(item)
}
}
7.2 OC 轻量泛型
objc
// OC 的轻量泛型 (Lightweight Generics)
// 只提供编译时检查,运行时会擦除
@interface Container<ObjectType> : NSObject
@property (nonatomic, strong) NSMutableArray<ObjectType> *items;
- (void)addItem:(ObjectType)item;
- (ObjectType)itemAtIndex:(NSUInteger)index;
@end
// Swift 中可以使用类型参数
let container = Container<NSString>()
container.addItem("Hello")
let item: String = container.item(at: 0) as String
八、错误处理互操作
8.1 Swift Error 与 NSError
swift
// Swift Error 自动桥接到 NSError
// 1. 定义 Swift Error
enum NetworkError: Error {
case noConnection
case timeout
case invalidResponse(code: Int)
}
// 2. 暴露给 OC
class NetworkManager: NSObject {
@objc func fetchData() throws -> Data {
throw NetworkError.timeout
}
}
// OC 中使用:
// NSError *error = nil;
// NSData *data = [manager fetchDataAndReturnError:&error];
// if (error) {
// NSLog(@"Error: %@", error);
// }
// 3. 自定义 NSError 信息
extension NetworkError: LocalizedError {
var errorDescription: String? {
switch self {
case .noConnection:
return "No internet connection"
case .timeout:
return "Request timed out"
case .invalidResponse(let code):
return "Invalid response: \(code)"
}
}
}
extension NetworkError: CustomNSError {
static var errorDomain: String {
return "com.app.NetworkError"
}
var errorCode: Int {
switch self {
case .noConnection: return 1001
case .timeout: return 1002
case .invalidResponse(let code): return code
}
}
var errorUserInfo: [String: Any] {
return [
NSLocalizedDescriptionKey: errorDescription ?? "Unknown error"
]
}
}
8.2 OC 方法的错误处理
swift
// OC 方法在 Swift 中变成 throws
// OC 定义:
// - (BOOL)saveData:(NSData *)data error:(NSError **)error;
// - (NSData *)loadDataWithError:(NSError **)error;
// Swift 使用:
let manager = DataManager()
// 返回 Bool 的变成 throws -> Void
do {
try manager.save(data)
} catch {
print("Save failed: \(error)")
}
// 返回对象的变成 throws -> Object
do {
let data = try manager.loadData()
// 使用 data
} catch {
print("Load failed: \(error)")
}
// 或使用 try?
let data = try? manager.loadData() // 失败返回 nil
// 或使用 try!(确定不会失败时)
let data = try! manager.loadData() // 失败会崩溃
九、Selector 与方法引用
9.1 #selector 的使用
swift
// #selector 需要 @objc 方法
class ViewController: UIViewController {
@objc func handleTap(_ sender: UITapGestureRecognizer) {
print("Tapped")
}
@objc func handleButton(_ sender: UIButton) {
print("Button pressed")
}
override func viewDidLoad() {
super.viewDidLoad()
// 使用 #selector
let tap = UITapGestureRecognizer(
target: self,
action: #selector(handleTap(_:))
)
view.addGestureRecognizer(tap)
let button = UIButton()
button.addTarget(
self,
action: #selector(handleButton(_:)),
for: .touchUpInside
)
}
}
// 获取方法的 Selector
let selector = #selector(ViewController.handleTap(_:))
print(selector) // handleTap:
9.2 方法的 Selector 命名
swift
class Calculator: NSObject {
// 无参数
@objc func reset() { }
// Selector: reset
// 单参数
@objc func add(_ value: Int) { }
// Selector: add:
// 多参数
@objc func add(_ a: Int, to b: Int) { }
// Selector: add:to:
// 自定义名称
@objc(calculateSumOf:and:)
func sum(_ a: Int, _ b: Int) -> Int { return a + b }
// Selector: calculateSumOf:and:
// 重载时需要类型注解
@objc func process(_ value: Int) { }
@objc func process(_ value: String) { }
func test() {
let intSelector = #selector(process(_:) as (Int) -> Void)
let stringSelector = #selector(process(_:) as (String) -> Void)
}
}
9.3 performSelector
swift
class Handler: NSObject {
@objc func handleAction() {
print("Action handled")
}
@objc func handleAction(with param: String) {
print("Action: \(param)")
}
}
let handler = Handler()
// 无参数
handler.perform(#selector(Handler.handleAction))
// 带参数
handler.perform(#selector(Handler.handleAction(with:)), with: "test")
// 延迟执行
handler.perform(
#selector(Handler.handleAction),
with: nil,
afterDelay: 1.0
)
// 在指定线程执行
handler.performSelector(
onMainThread: #selector(Handler.handleAction),
with: nil,
waitUntilDone: false
)
十、高级混编技巧
10.1 Swift 调用 C/C++
swift
// Swift 不能直接调用 C++,需要通过 OC 桥接
// 1. C 函数可以直接调用
// C header (MyCFunctions.h)
// int add(int a, int b);
// void processData(const char *data, size_t length);
// Bridging header
// #import "MyCFunctions.h"
// Swift
let result = add(1, 2)
let data = "Hello"
data.withCString { ptr in
processData(ptr, data.count)
}
// 2. C++ 需要 OC 包装
// CppWrapper.h
@interface CppWrapper : NSObject
- (void)processCpp:(NSString *)data;
@end
// CppWrapper.mm (注意 .mm 扩展名)
#import "CppWrapper.h"
#include "MyCppClass.hpp"
@implementation CppWrapper {
MyCppClass *_cppObject;
}
- (instancetype)init {
self = [super init];
if (self) {
_cppObject = new MyCppClass();
}
return self;
}
- (void)dealloc {
delete _cppObject;
}
- (void)processCpp:(NSString *)data {
std::string cppString = [data UTF8String];
_cppObject->process(cppString);
}
@end
// Swift 使用
let wrapper = CppWrapper()
wrapper.processCpp("Hello C++")
10.2 运行时检查与反射
swift
// Swift 对象的运行时检查
class MyClass: NSObject {
@objc dynamic var name: String = ""
@objc func doSomething() { }
}
// 1. 检查类和协议
let obj = MyClass()
if obj.isKind(of: MyClass.self) {
print("Is MyClass")
}
if obj.responds(to: #selector(MyClass.doSomething)) {
print("Responds to doSomething")
}
if obj.conforms(to: NSCopying.self) {
print("Conforms to NSCopying")
}
// 2. 获取类信息
let className = NSStringFromClass(type(of: obj))
print(className) // _TtC8MyModule7MyClass
// 3. 动态方法调用
let selector = NSSelectorFromString("doSomething")
if obj.responds(to: selector) {
obj.perform(selector)
}
// 4. KVC
obj.setValue("Test", forKey: "name")
let value = obj.value(forKey: "name")
// 5. Mirror (Swift 反射)
let mirror = Mirror(reflecting: obj)
for child in mirror.children {
print("\(child.label ?? "?"): \(child.value)")
}
10.3 避免常见混编陷阱
swift
// 陷阱 1: 可选值与 nil
class DataManager: NSObject {
// OC 返回 nil 时,Swift 必须处理
@objc func getData() -> NSData? {
return nil
}
}
// Swift 使用时需要解包
if let data = manager.getData() {
// 使用 data
}
// 陷阱 2: 集合类型元素
class Container: NSObject {
// OC 集合可以包含任意类型
@objc var items: NSMutableArray = []
}
// Swift 中需要类型转换
let container = Container()
container.items.add("string")
container.items.add(NSNumber(value: 42))
// 取出时需要转换
if let first = container.items.firstObject as? String {
print(first)
}
// 陷阱 3: 类型推断问题
// OC:
// @property (nonatomic, strong) id someObject;
// Swift 中是 Any,需要转换
let obj: Any = container.someObject
if let string = obj as? String {
// ...
}
// 陷阱 4: 初始化器
class SwiftClass: NSObject {
let requiredProperty: String
// 必须提供 @objc init
@objc init(property: String) {
self.requiredProperty = property
super.init()
}
// 如果有便利初始化器
@objc convenience init() {
self.init(property: "default")
}
}
// 陷阱 5: 闭包逃逸
class NetworkManager: NSObject {
// 必须标记 @escaping
@objc func request(completion: @escaping (Data?) -> Void) {
DispatchQueue.global().async {
completion(nil)
}
}
}
十一、性能优化建议
11.1 减少桥接开销
swift
// 1. 批量处理而非逐个桥接
// ❌ 低效
func processOCItems(_ array: NSArray) {
for item in array {
if let str = item as? String {
// 每次循环都进行类型检查和转换
process(str)
}
}
}
// ✅ 高效
func processOCItems(_ array: NSArray) {
// 一次性转换
let swiftArray = array as? [String] ?? []
for str in swiftArray {
process(str)
}
}
// 2. 避免频繁的 String 桥接
// ❌ 频繁桥接
func appendToOCString(_ nsString: NSMutableString) {
for i in 0..<1000 {
nsString.append(String(i)) // 每次创建临时 String
}
}
// ✅ 使用 OC 原生方法
func appendToOCString(_ nsString: NSMutableString) {
for i in 0..<1000 {
nsString.append("\(i)" as NSString) // 直接使用 NSString
}
}
// 3. 减少不必要的 @objc
class PureSwiftClass {
// 不需要 OC 互操作的类不要加 @objc
func internalMethod() { }
}
11.2 方法调度优化
swift
// 1. 使用 final 减少动态调度
final class OptimizedManager {
func process() { } // 静态调度
}
// 2. 使用 private 启用优化
class Manager {
private func helperMethod() { } // 可被编译器优化
}
// 3. 最小化 @objc 范围
class ViewController: UIViewController {
// 只有需要 OC 交互的才用 @objc
@objc func handleButton() { }
// 纯 Swift 方法不用 @objc
func updateUI() { }
}
// 4. 避免不必要的 @objc dynamic
class ViewModel: NSObject {
// 需要 KVO 才用 dynamic
@objc dynamic var observableProperty: String = ""
// 不需要 KVO 就不用 dynamic
@objc var normalProperty: String = ""
}
十二、总结
12.1 混编核心要点
┌─────────────────────────────────────────────────────────────────────┐
│ Swift/OC 混编核心要点 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 【桥接文件】 │
│ • xxx-Bridging-Header.h: Swift 访问 OC │
│ • xxx-Swift.h: OC 访问 Swift (自动生成) │
│ │
│ 【@objc 相关】 │
│ • @objc: 暴露给 OC 运行时 │
│ • @objcMembers: 批量暴露所有成员 │
│ • @objc dynamic: 启用完整的消息调度 │
│ • @objc(name): 自定义 OC 中的名称 │
│ │
│ 【类型桥接】 │
│ • String ⟷ NSString: 零开销 │
│ • Array ⟷ NSArray: 零开销 │
│ • Int ⟷ NSNumber: 需要装箱 │
│ • struct/enum: 不能直接桥接 │
│ │
│ 【方法调度】 │
│ • 静态调度: struct, final class, private │
│ • V-Table: class 普通方法 │
│ • 消息调度: @objc dynamic, NSObject 子类的 @objc 方法 │
│ │
│ 【不能用 @objc】 │
│ • 结构体、枚举(非Int) │
│ • 泛型类型 │
│ • 元组 │
│ • 嵌套类型 │
│ • Swift 独有特性 (默认参数等) │
│ │
│ 【性能建议】 │
│ • 最小化 @objc 使用范围 │
│ • 批量桥接而非逐个 │
│ • 使用 final/private 启用优化 │
│ • 只在需要时使用 @objc dynamic │
│ │
└─────────────────────────────────────────────────────────────────────┘
12.2 最佳实践清单
□ 明确区分需要 OC 互操作和纯 Swift 的代码
□ 只在必要时使用 @objc,优先使用纯 Swift
□ 继承 NSObject 时考虑是否真的需要
□ 使用 final class 和 struct 优化性能
□ 正确处理可选值和类型转换
□ 闭包记得使用 [weak self] 或 [unowned self]
□ 理解三种方法调度方式及其适用场景
□ C++ 代码通过 OC 包装器暴露给 Swift
□ 合理使用协议:纯 Swift 协议 vs @objc 协议
□ 定期检查生成的 xxx-Swift.h 确保接口正确