Weak Reference(弱引用)— 是一种不会增加ARC中对象持有计数器的引用。根据 Apple Swift Language Guide, 2026,弱引用使用关键字 weak 声明,并且始终具有可选类型。当对象被释放时,所有指向它的弱引用会自动设置为nil,这可以防止悬空指针,并使弱引用成为打破循环引用的安全机制。
要点
weak;类型始终为可选类型 (?)Weak Reference — 是ARC(自动引用计数)中非拥有的对象引用。与增加对象引用计数并保证其生命的strong引用不同,weak引用允许对象即使仍有引用也能被释放。释放后,weak引用会自动设置为nil — 这称为自动归零弱引用。
自动归零弱引用是Swift和Objective-C运行时的关键特性。当对象的引用计数达到零且对象被释放时,运行时遍历对该对象的所有弱引用(存储在特殊的弱引用表中)并将它们设置为nil。这保证了通过弱引用无法访问已释放的内存(释放后使用)— 任何读取都将返回nil。
根据Apple WWDC 2012 Session 406,自动归零弱引用消除了与悬空指针相关的一整类崩溃错误,这些错误在手动内存管理(MRR)中很常见。在MRR中,弱引用仅以 __unsafe_unretained 的形式存在 — 它们不会归零,访问已释放的对象会导致EXC_BAD_ACCESS。
让我们看看Apple生态系统中两种语言的弱引用声明语法。尽管运行时相同,但语法不同,不过语义相同。
在Swift中,弱引用使用关键字 weak 在var之前声明。类型必须始终是可选类型(Type?),因为引用随时可能归零。常量(let)不能是weak — 只能是变量。
class ViewController: UIViewController {
// weak属性:仅var,仅optional
weak var delegate: ViewControllerDelegate?
weak var parentView: UIView?
weak var completionHandler: ((Bool) -> Void)? // ⚠️ 闭包不存储weak
// ⬆️ 错误:weak仅适用于class类型,不适用于closure
}
重要:weak仅适用于类实例(class-types)、AnyObject和继承自AnyObject的协议。Struct、enum和closure不能是weak — 它们是值类型,不参与ARC。
在Objective-C中,weak属性通过 __weak 属性或property中的 weak 修饰符声明:
// Objective-C: weak属性
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end
// 局部weak变量
__weak MyObject *weakRef = someStrongObject;
Objective-C运行时也提供自动归零弱引用,但额外阻止将weak与C结构和某些Core Foundation对象一起使用。对于它们使用 __unsafe_unretained — 没有自动归零。
弱引用 — 不是通用解决方案,而是特定场景的工具。到处使用weak会导致过度复杂化并降低可读性。让我们看看正确的使用场景。
委托 — weak的主要场景。所有者对象(例如UITableView)对自己持有强引用,而委托(UIViewController)不应拥有表格。Apple SDK保证所有delegate和dataSource都是weak的。对于自己的协议,始终使用 weak var delegate。
当子对象需要引用父对象时(例如ChildViewController访问协调器),使用弱引用。父对象拥有子对象(strong),子对象观察父对象(weak)— 排除循环引用。
捕获列表 [weak self] — 避免存储在类属性中的闭包中循环引用的标准方式。如果self可能在闭包完成前释放 — weak self是必需的。
| 场景 | Weak | Strong |
|---|---|---|
| Delegate | ✅ 总是weak | ❌ 循环引用 |
| Parent → Child | ❌ 不需要(父对象应拥有) | ✅ Strong |
| Child → Parent | ✅ Weak | ❌ 循环引用 |
| 异步回调 | ✅ [weak self] | ❌ 循环引用风险 |
| 强关联(owned) | ❌ unowned | ✅ Strong |
通用规则:如果对象A拥有B(A → B strong),那么B → A应该是weak或unowned。强引用的方向应始终从所有者到下属。
weak和unowned都不增加引用计数,但在对象释放后的行为不同。它们之间的选择是生命周期保证的问题。
Weak:自动归零(nil),类型始终为可选类型,使用前需要解包。安全 — 访问nil不会导致崩溃。
Unowned:不会归零,类型为非可选类型。如果对象已被释放,unowned引用变为悬空指针 — 访问它会导致运行时崩溃。Unowned假定对象的生命不短于引用方。
Weak 选择条件:对象可能随时被释放(屏幕关闭后的委托),你不控制对象的生命周期,或对保证有疑问。Weak — 通用安全选择。
Unowned 选择条件:对象保证不会比引用方更早释放(例如Customer → CreditCard,卡不能没有客户)。Uowned提供无需解包的非可选API,在代码中更方便。
class Order {
let id: Int
var items: [Item] = []
init(id: Int) { self.id = id }
// 强关联:Order拥有Item
func addItem(name: String) {
let item = Item(name: name, order: self)
items.append(item)
}
}
class Item {
let name: String
unowned let order: Order // ✅ unowned — Item不能没有Order
init(name: String, order: Order) {
self.name = name
self.order = order
}
}
// weak示例:没有生命周期保证的委托
protocol NetworkServiceDelegate: AnyObject {
func didReceiveResponse(data: Data)
}
class NetworkService {
weak var delegate: NetworkServiceDelegate? // ✅ weak — 委托可以离开
}
在示例中,Item使用unowned,因为订单项不能没有订单本身存在 — 生命周期保证是铁定的。NetworkService使用weak,因为委托(例如ViewController)可能随时关闭和释放。
弱引用 — 强大的工具,但有限制,理解这些限制对于在iOS开发中正确应用很重要。
弱引用比强引用慢:每次访问时运行时检查对象是否已释放(在弱引用表中查找)。在绝大多数场景中差异不明显,但在数百万次访问的热循环中,weak可能成为瓶颈。对于高负载场景使用strong并重构架构。
Struct, enum, tuple — 不参与ARC的值类型。尝试声明weak struct会导致编译错误。要存储对值类型的弱引用,请使用class类型的包装器或闭包。
自动归零弱引用是线程安全的:如果对象在一个线程上释放,弱引用会在所有线程上原子地归零。然而,读取弱引用与使用之间的间隙可能导致竞态条件 — 对象在获取弱引用和使用它之间被释放。解决方案:强捕获弱引用到局部变量。
// 多线程中weak的竞态条件
func performAsync() {
weak var weakSelf = self
queue.async {
// ⚠️ weakSelf在检查和使用之间可能为nil
if weakSelf != nil {
weakSelf!.doSomething() // 如果变为nil则崩溃
}
}
}
// ✅ 修复:在使用期间强捕获
func performAsyncSafe() {
queue.async { [weak self] in
guard let strongSelf = self else { return }
strongSelf.doSomething() // strongSelf — 局部强引用
}
}
在安全变体中,weak self被捕获,然后立即解包到局部强变量 strongSelf。如果self仍然存活 — 它将在块执行期间保持存活。如果没有 — guard被触发,代码不会执行。这个习惯用法是Swift中异步闭包的标准模式。
IBOutlet在Interface Builder中应该是weak的,因为视图层次结构已经持有对subview的强引用。在控制器中重复强引用不会创建循环引用,但多余。对outlet的弱引用 — Apple的建议,尽管许多开发人员为了简化代码而使用strong。
常见问题
不能,weak只能指向现有对象或nil。创建新对象时,首先获得强引用(通过初始化器),然后才能分配弱引用。初始时的weak nil — 正常状态。
Weak基于ARC,ARC只管理引用类型(类)。值类型(struct、enum)在赋值时复制,没有引用计数。对于值类型的弱关联,使用闭包或带有weak属性的class包装器。
每次访问弱引用时在运行时表中执行查找。在具有数百万次迭代的循环中,这可能比强引用慢2–5倍。对于热路径,在循环之前将weak复制到局部强变量。
当对象的所有强引用丢失时 — 在作用域结束时、重新设置属性时、关闭屏幕时。在多线程环境中,这可能在两行代码之间发生。始终通过guard let或if let检查weak。
语义相同:两者都提供自动归零弱引用。区别:Swift需要可选类型和var,Objective-C使用property修饰符。Objective-C还支持 __unsafe_unretained — 无自动归零的弱引用(悬空指针风险)。
总结
weak var + 可选类型;仅类类型和AnyObject协议我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。