简单来说,它们在最终目的(打破循环引用)上是等价的,但在执行细节和语法语义上,Swift 的 [weak self] 比 OC 的 __weak 走得更远,也更安全。
我们可以从以下三个维度来拆解它们的本质差异:
1. 定义时机与作用域 (Declaration vs. Capture List)
-
OC (
__weak) 是“手动预处理”:在 OC 中,你必须在 Block 外部定义一个全新的局部变量(如
weakSelf)。Block 捕获的是这个新变量,而不是原始的self。如果忘记写这行代码而在 Block 里直接用了self,编译器通常只会给一个警告,循环引用依然会发生。 -
Swift (
[weak self]) 是“捕获声明”:Swift 使用 Capture List(捕获列表) 。它直接在闭包的头部定义捕获规则。这意味着
self在进入闭包那一刻,其捕获语义就被改变了。它不需要创建中间变量,语法上更加内聚。
2. 类型系统的安全性 (Optionality)
这是两者最显著的差异:
-
OC:指针透明性
在 OC Block 内部,
weakSelf看起来和普通的self没区别。你可以直接调用方法:[weakSelf doSomething]。如果self已经释放,weakSelf为nil,根据 OC 特性,向nil发消息什么都不会发生。 -
Swift:强制 Optional
一旦在 Capture List 中声明了
weak self,在闭包内部self的类型就从Self变成了Self?(Optional) 。编译器会强制你处理nil的情况(使用guard let或self?)。这种“类型降级”在编译阶段就逼你考虑对象提前释放的可能性。
3. “强引用提升”的实现方案 (Strong-Weak Dance)
为了防止代码执行到一半 self 突然消失,我们通常需要临时把弱引用变回强引用。
Objective-C 方案:
需要手动再次定义一个 __strong 变量:
Objective-C
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
Swift 方案:
Swift 提供了更优雅的语法糖。在早期的 guard let self = self 之后,Swift 5.7+ 允许你直接写:
Swift
guard let self = self else { return }
// 此后 self 在闭包作用域内变回了强引用的非可选类型
本质差异: Swift 的这种“重新绑定”受语言编译器保护,确保了在闭包执行期间,self 的生命周期会被临时延长,直到闭包结束。
4. 性能上的细微差别
- OC: 依赖于 Runtime 的全局弱引用表(SideTable)。每次
weakSelf的读写都涉及一次查表和可能的加锁操作。 - Swift: 虽然同样依赖类似的弱引用计数机制,但 Swift 的内存管理(引用计数存储在对象头部的
inline空间或外部的SideTable中)与编译器高度优化。由于 Swift 闭包捕获列表在编译期就确定了布局,在某些情况下,编译器能比 OC 产生更高效的寻址代码。
总结对照表
| 特性 | Objective-C __weak | Swift [weak self] |
|---|---|---|
| 位置 | 闭包外部手动创建 | 闭包头部的捕获列表 |
| 类型 | 原始类型指针 (可赋 nil) | Optional 类型 (Self?) |
| 编译器检查 | 弱(容易漏写导致强引用) | 强(强制处理可选值) |
| 空处理 | 向 nil 发消息 (无操作) | 必须通过 Optional Binding 处理 |