Objective-C 与 Swift 互操作 2026:什么仍然能用,什么悄悄坏了
苹果官方信号是"全用 Swift"。2026 年的现实是任何超过 5 年的 iOS 老项目都还有 ObjC,你依赖的任何框架也可能有。这是让两者共处不出周周事故的实战指南。
2026 现状
- 苹果框架 — 几乎全是 Swift-first API。UIKit、AVFoundation、Core Data,连老坚守 Network.framework 都对 Swift 友好。
- 主要 OSS 库 — 多数已 Swift 重写、出了 Swift 包装、或被 Swift 原生等价物替代。
- 你的老代码 — 还是 ObjC,可能还能跑,可能还在改。
互操作层没被废弃,短期也不会。但 Swift 6 严格并发引入了新的失败模式,必须知道。
桥接头文件 — 依然是路
Swift target 消费 ObjC 代码:
// MyApp-Bridging-Header.h #import "LegacyAuthManager.h" #import "ObjCViewController.h"
Xcode 在你第一个 ObjC 文件加入 Swift 项目时自动生成。import 想暴露的 header 即可。@property 和方法自动变成 Swift API:
let mgr = LegacyAuthManager.shared mgr.signIn(withUsername: "alice", password: "...") { result in /* ... */ }
ObjC selector:withBlah: → Swift signIn(withUsername:password:)。NS_SWIFT_NAME(...) 让你覆盖默认名当它丑时。
把 Swift 暴露给 ObjC
Swift 类型在 ObjC 可见的条件:
- 类继承
NSObject(或其他 ObjC 可见类)。 - 成员标
@objc(或类级@objcMembers)。 - 类型 ObjC 兼容(
Int、String、class — 不能是带关联值的 Swift enum、泛型、struct)。
@objc(NewAccountManager) class AccountManager: NSObject { @objc func signIn(username: String, password: String, completion: @escaping (Bool) -> Void) {} }
Xcode 给每个 target 自动生成 YourTarget-Swift.h——从 ObjC 那边 import 即可。
Swift 6 严格并发改了什么
这是 2026 年最多团队踩坑的地方。
Swift 6 要求每次跨 actor 调用可 await、每个共享值是 Sendable。ObjC 既没有 actor 也没有 Sendable 概念,结果:
- ObjC 导入的类型默认不是 Sendable。
- ObjC 委托回调要进
@MainActorSwift 类时,ObjC 声明常需要NS_SWIFT_UI_ACTOR。 - 跨边界的 closure 需要显式
@Sendable。
集成 ObjC 委托到 SwiftUI view model 常用解法:
@protocol LegacyManagerDelegate <NSObject> - (void)managerDidFinish:(LegacyManager *)manager NS_SWIFT_UI_ACTOR; @end
NS_SWIFT_UI_ACTOR 告诉 Swift 这个回调总在主 actor 上 — 编译器就不抱怨你访问 @MainActor 状态了。
对不能改的 ObjC 类,做包装:
@MainActor final class ManagerBridge: NSObject, LegacyManagerDelegate { // 包装老 manager,对外暴露 Swift 原生 @MainActor API }
仍然咬人的内存规则
- ObjC
weak对继承 NSObject 的 Swift 类正常;对纯 Swift class 不行。 @property (copy)NSString → Swift String,复制隐式,无意外。@property (assign)接对象是坑。迁到 weak/strong。- Block 桥到 Swift closure 内存语义一致。ObjC 里的循环引用,Swift 里也是。
迁移:渐进,不要大爆炸
5 万行以上 ObjC 项目:
- 不要重写。 日历时间从来不值得。
- 只在新 feature 用 Swift。 把需要的 ObjC API 包成符合 Swift 习惯的门面。
- 碰到叶子文件时顺手迁。 跑单测失败要打开文件时,就那时迁。
find . -name "*.m" | wc -l当慢烧指标。 别当 sprint 目标。
我见过成功的团队都拒绝自上而下重写。试图大爆炸的团队 3 年后还在迁。
常见互操作 bug 与修法
| Bug | 原因 | 修 |
|---|---|---|
| Swift 类 ObjC 不可见 | 没继承 NSObject | 继承或公开 API 标 @objc |
@objc enum ObjC 看不到 | 用了关联值 | 改成 Int-backed enum |
| ObjC 协议可选方法被无视调用 | ObjC nil-messaging 静默 | 用 respondsToSelector 或迁到 Swift |
| 桥接头改了没生效 | 构建缓存 | 清 derived data,必要时重启 Xcode |
| 严格并发抱怨委托方法 | 缺 NS_SWIFT_UI_ACTOR | 在 ObjC 声明加上 |
反模式
@objcMembers因为懒得写@objc。 能用但暴露面比需要的大,按成员加@objc。- 从 ObjC 用运行时反射进 Swift 内部。 跨编译器版本脆弱。
- 跨语言边界双向绑定可变状态。 传快照、发事件,别共享。
- "Swift 是未来"重写跑得好的 ObjC 模块。 不在新功能路径上就别动。
一句话总结
- 互操作仍是标准,仍然健康。
- 桥接头和
*-Swift.h跟十年前一样还在用。 - Swift 6 严格并发在边界加了新规则——学会
NS_SWIFT_UI_ACTOR和@Sendable。 - 不要大批量重写 ObjC,按叶子按需迁。
- ObjC 不是烂代码的标志,是早赚到钱的标志。
相关阅读
Swift 并发 2026:async/await、Actor 与 Sendable 严格检查
务实的 Swift 并发指南 — async/await 用法、Actor 与 @MainActor、结构化并发 TaskGroup、Swift 6 strict Sendable 检查及老回调 API 迁移。
TypeScript 类型体操:什么时候值,什么时候在炫技
务实的 TypeScript 高级类型指南 — mapped types、conditional types、template literal types 真正能给你什么,什么时候用,什么时候应该退回到朴素代码。
Kubernetes 资源 requests / limits 实战:不会把生产搞挂的设法
怎么在生产里实际设 Kubernetes CPU 与内存的 requests/limits — QoS 类、CPU 节流、OOM kill、那些害公司钱的差别,以及好使的模式。