oc和swift相互调用
可以。OC 和 Swift 混编主要分两个方向: 1. Swift 调用 Objective-C:通过桥接头文件。 2. Objective-C 调用 Swift:通过 Xcode 自动生成的 `项目名-Swift.h`。 ## 一、Swift 调用 Objective-C 假设有一个 OC 类。 `Person.h`: ```objc #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface Person : NSObject @property (nonatomic, copy) NSString *name; - (instancetype)initWithName:(NSString *)name; - (void)sayHello; @end NS_ASSUME_NONNULL_END ``` `Person.m`: ```objc #import "Person.h" @implementation Person - (instancetype)initWithName:(NSString *)name { self = [super init]; if (self) { _name = [name copy]; } return self; } - (void)sayHello { NSLog(@"Hello,我是 %@", self.name); } @end ``` ### 1. 创建桥接头文件 如果在 Swift 项目里第一次创建 `.m` 文件,Xcode 通常会询问: ```text Would you like to configure an Objective-C bridging header? ``` 选择: ```text Create Bridging Header ``` 然后会生成: ```text 项目名-Bridging-Header.h ``` 在里面导入 OC 头文件: ```objc #import "Person.h" ``` ### 2. Swift 直接调用 ```swift let person = Person(name: "小明") person.sayHello() print(person.name) ``` 不需要在 Swift 文件里再写: ```swift import Person // 不需要 ``` 因为桥接头已经将 `Person.h` 暴露给整个 Swift Target。 ### 3. 没有自动创建桥接头怎么办? 自己新建一个头文件: ```text MyApp-Bridging-Header.h ``` 内容: ```objc #import "Person.h" #import "NetworkManager.h" #import "LegacyViewController.h" ``` 然后打开: ```text Target → Build Settings → Objective-C Bridging Header ``` 填写桥接头相对于项目目录的路径,例如: ```text MyApp/MyApp-Bridging-Header.h ``` 注意不要填写绝对路径。 --- ## 二、Objective-C 调用 Swift Swift 类需要能暴露给 Objective-C。 ```swift import Foundation @objcMembers final class UserService: NSObject { var userName: String = "" func login(name: String, completion: @escaping (Bool) -> Void) { userName = name print("登录用户:\(name)") completion(true) } func logout() { userName = "" } } ``` 这里有两个重点: ```swift NSObject ``` 让 Swift 类进入 Objective-C 运行时体系。 ```swift @objcMembers ``` 自动将该类中兼容 OC 的成员暴露给 Objective-C。 ### 在 `.m` 文件中导入自动生成的头文件 ```objc #import "MyApp-Swift.h" ``` 然后直接调用: ```objc UserService *service = [[UserService alloc] init]; [service loginWithName:@"小明" completion:^(BOOL success) { NSLog(@"登录结果:%@", success ? @"成功" : @"失败"); }]; [service logout]; ``` `MyApp-Swift.h` 不是真实存在于源码目录里的文件,而是 Xcode 编译时自动生成的。 它的名字取决于 Target 的模块名称,不一定等于工程文件名。可以在这里查看: ```text Target → Build Settings → Product Module Name ``` 假设值是: ```text MyApplication ``` 那么就导入: ```objc #import "MyApplication-Swift.h" ``` ## 三、只暴露个别 Swift 方法 如果不想整个类都使用 `@objcMembers`,可以分别添加 `@objc`: ```swift final class PaymentManager: NSObject { @objc var amount: Double = 0 @objc func pay() { print("支付 \(amount) 元") } // 只给 Swift 使用 func loadSwiftOnlyData() async throws -> Data { Data() } } ``` OC 调用: ```objc PaymentManager *manager = [[PaymentManager alloc] init]; manager.amount = 99; [manager pay]; ``` 如果希望 OC 看到一个更自然的方法名,可以指定 selector: ```swift @objc(loginWithUserName:) func login(userName: String) { print(userName) } ``` OC: ```objc [service loginWithUserName:@"小明"]; ``` ## 四、哪些 Swift 写法不能直接给 OC 调用? Objective-C 无法直接理解一部分纯 Swift 特性,例如: - `struct` - Swift 风格的 `enum`,尤其是带关联值的枚举 - 泛型方法和泛型类型 - `async/await` - 元组 - Swift-only protocol - 一些复杂的闭包类型 - 默认参数 - 方法重载在映射后可能产生冲突 例如下面不能直接暴露: ```swift struct User { let name: String } func request<T>() -> T { // ... } ``` 通常需要包装成 OC 能理解的类型: ```swift @objcMembers final class OCUser: NSObject { let name: String init(name: String) { self.name = name super.init() } } ``` ### async 方法的兼容包装 Swift: ```swift func fetchUser() async throws -> String { "小明" } ``` 给 OC 包一层 completion: ```swift @objc func fetchUser( completion: @escaping (NSString?, NSError?) -> Void ) { Task { do { let name = try await fetchUser() completion(name as NSString, nil) } catch { completion(nil, error as NSError) } } } ``` ## 五、OC 枚举与 Swift 推荐使用 `NS_ENUM`: ```objc typedef NS_ENUM(NSInteger, LoginState) { LoginStateUnknown, LoginStateLoading, LoginStateSuccess, LoginStateFailure }; ``` Swift 会把它识别为枚举: ```swift let state: LoginState = .success ``` Swift 枚举想暴露给 OC,需要使用 `Int` 原始值: ```swift @objc enum PaymentState: Int { case pending case success case failure } ``` OC: ```objc PaymentState state = PaymentStateSuccess; ``` ## 六、Block 和 Swift 闭包 OC: ```objc typedef void (^LoginCompletion)(BOOL success, NSString * _Nullable message); - (void)loginWithCompletion:(LoginCompletion)completion; ``` Swift 调用时会转换成闭包: ```swift manager.login { success, message in print(success) print(message ?? "") } ``` Swift 暴露给 OC: ```swift @objc func loadData( completion: @escaping (NSData?, NSError?) -> Void ) { // ... } ``` OC: ```objc [manager loadDataWithCompletion:^(NSData *data, NSError *error) { // ... }]; ``` ## 七、一个好记的关系 ```text Swift → OC 桥接头:项目名-Bridging-Header.h 桥接头里 import OC 的 .h ``` ```text OC → Swift 自动头:ProductModuleName-Swift.h Swift 类继承 NSObject,并使用 @objc 或 @objcMembers ``` 常见报错排查: - 检查文件的 `Target Membership` 是否勾选。 - 检查 `Product Module Name`,不要凭项目显示名称猜 `xxx-Swift.h`。 - 桥接头只导入 `.h`,不要导入 `.m`。 - `项目名-Swift.h` 通常只在 `.m` 中导入,不要随意放进公共 `.h`。 - Swift 类即使写了 `public`,也不代表 OC 能调用;仍需兼容 Objective-C 运行时并使用 `@objc`。
在北京的灯中,有一盏是我家的。这个梦何时可以实现?哪怕微微亮。北京就像魔鬼训练营,有能力的留,没能力的走……

浙公网安备 33010602011771号