Unowned Reference(无主引用)——是 Swift 中一种不持有的引用,它不增加对象的 retain count,与 weak 不同,它在对象释放后不会设置为 nil。根据 Apple Swift Language Guide, 2026,当确保对象至少与引用它的对象存活时间相同时使用 unowned。与 Weak Reference 不同,unowned 不需要 unwrap——它是非可选类型,使代码更简洁,但将生命周期保证的责任交给了开发者。
要点
Unowned Reference——是 ARC 中对对象的一种不持有引用,不增加其 retain count。与 weak 不同,unowned 在对象释放后不会归零:它继续指向已经释放的内存区域。访问这样的引用会导致 EXC_BAD_ACCESS 运行时崩溃。
“无主”一词反映了语义:对象存在,但没有人对其生命周期负责。开发者明确声明:“我保证只要我引用它,这个对象就会存活”。编译器不检查这个保证——这是开发者层面的契约。
根据 Swift.org Documentation, 2026,在保证生命周期的场景中,unowned 引用比 weak 更受欢迎,因为:它们不需要可选类型(更干净的代码),不需要 unwrap(更少的 force-unwrap 或 guard let),并且没有维护归零 weak 表的开销。然而,任何违反契约的行为——都会崩溃。
在 Swift 中,unowned 引用使用关键字 unowned 在 let 或 var 之前声明。与 weak 不同,unowned 可以是 let 和 var,并且不需要可选类型。这个特性使 unowned 对于根据领域逻辑不能为 nil 的引用很方便。
class Country {
let name: String
var capital: City! // 初始化后将设置
init(name: String) { self.name = name }
}
class City {
let name: String
unowned let country: Country // unowned let — 生命周期保证
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
// 使用
let france = Country(name: "France")
let paris = City(name: "Paris", country: france)
france.capital = paris
// Country → City (strong), City → Country (unowned) — 无 retain cycle
在这个例子中,City unowned let country——城市不能没有国家而存在。如果国家消失,城市(和引用)就失去了意义。从语义上讲,这是 unowned 的理想情况:生命周期保证存在,不需要 optional,不会产生 retain cycle。
unowned var 是允许的但不太常见。当引用可能被替换时使用(例如,将子节点重新连接到不同的父节点)。替换时,旧对象的释放是外部所有者的责任。
在 Swift 5.0+ 中添加了对 unowned optional 的支持(unowned let x: Type?)。这是一种折衷:unowned 保证如果引用不是 nil,则对象存活。释放时的行为——崩溃,与普通的 unowned 相同。
在 unowned 和 weak 之间选择——设计 Swift 架构时常见的决策之一。让我们看看每种情况的标准和建议。
| 标准 | Weak | Unowned |
|---|---|---|
| Optional | 是(Type?) | 否(Type) |
| 释放时归零 | 自动为 nil | 否(悬空指针风险) |
| 类型(let/var) | 仅 var | let 或 var |
| 性能 | weak 表的开销 | 最小(简单指针) |
| 安全性 | 安全(nil 被检查) | EXC_BAD_ACCESS 风险 |
| 生命周期保证 | 不需要 | 需要明确保证 |
如果对对象的生命周期有任何疑问,使用 weak。Weak 安全、易懂且不需要证明。仅当排除了对象可能提前释放的所有场景时,才使用 unowned。典型情况:没有父节点就不存在的子节点;同步执行的闭包;在其初始化器内引用对象。
根据 Airbnb Swift Style Guide, 2025,在大型代码库中建议默认使用 weak,而 unowned——仅附有解释生命周期保证的明确注释。这降低了重构时意外崩溃的风险。
闭包——在 parent-child 关系之后第二常见的 unowned 使用场景。当确保 self 比闭包存活得更久时,使用捕获列表 [unowned self]。让我们看看正确和错误的场景。
同步闭包——sorted、filter、map。它们立即在当前线程中执行,self 肯定存活。这里使用 unowned 的捕获列表是可以接受的,并且提供了更干净的代码。
class DataProcessor {
var items: [Int] = [3, 1, 4, 1, 5]
func processSorted() {
// unowned self — sorted 同步执行,self 保证存活
let sorted = items.sorted { [unowned self] a, b in
return self.customCompare(a, b)
}
}
func customCompare(_ a: Int, _ b: Int) -> Bool { return a < b }
}
异步闭包——带有延迟、网络请求、动画。Self 可能在闭包放置和执行之间被释放。这里 unowned self → 崩溃。使用 [weak self]。
class NetworkLoader {
func loadData() {
// 危险:异步闭包中的 unowned self
URLSession.shared.dataTask(with: url) { [unowned self] data, _, _ in
self.handleResponse(data) // 如果 self 被释放则 CRASH
}.resume()
}
func handleResponse(_ data: Data?) { }
// 正确:weak self + guard
func loadDataSafe() {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self else { return }
self.handleResponse(data)
}.resume()
}
}
记住规则:unowned self——仅用于立即执行的同步闭包。对于异步——始终 weak self + guard let。例外:如果您明确持有对对象的引用直到闭包完成(例如,通过在另一个变量中保存强捕获)。
Unowned——强大但危险的工具。让我们看看 unowned 可能导致崩溃的真实场景以及风险最小化的方法。
Unowned 的主要风险——业务逻辑变更导致生命周期保证不再成立。开发者重构代码:更改所有权、引入延迟释放、添加缓存——unowned 引用就变成了定时炸弹。编译器不会警告——只有在用户设备上崩溃。
建议:仅在生命周期保证明显且已记录时使用 unowned。为每个 unowned 添加注释:为什么这个引用是安全的以及在什么条件下可能被违反。
UIKit——unowned 的高风险区域。ViewController 在导航(pop、dismiss)、从内存卸载、方向更改时随时可能被释放。如果您将 ViewController 传递给带有 unowned self 的闭包——从后台返回或动画结束时,self 可能为 nil。
为减少使用 unowned 时的风险,请遵循以下规则:
// 示例:带有明确理由的已记录 unowned 引用
class InvoiceLineItem {
let productName: String
let price: Decimal
// unowned Invoice — InvoiceLineItem 不能没有 Invoice 而存在。
// Invoice 创建 Item 并在自身删除时删除它。
// 保证:Invoice 至少与 Item 存活时间相同。
unowned let invoice: Invoice
init(productName: String, price: Decimal, invoice: Invoice) {
self.productName = productName
self.price = price
self.invoice = invoice
}
}
// 这是强保证:Invoice 在 deinit 中删除所有 Item。
// 违反保证 = 需要修复的业务逻辑错误。
保证的记录——专业标准。在大型项目中(Airbnb、Uber),代码审查需要对每个 unowned 进行理由说明。如果保证不明显——使用 weak。对 unowned 的注释帮助未来的开发者理解为什么这里不是 weak,以及什么条件可能破坏保证。
常见问题
运行时崩溃,EXC_BAD_ACCESS。Swift 在访问时不检查 unowned 引用的有效性——它只是一个“原始”指针。如果对象已被释放,内存被覆盖,访问它会导致崩溃。这是一个不可捕获的异常(不是 try-catch)。
可以,如果协议继承自 AnyObject。Unowned 适用于所有引用类型:类、AnyObject 协议、Objective-C 对象。值类型(struct、enum)不支持 unowned,因为它们不参与 ARC。
当生命周期保证是绝对且明显的时候——unowned 从设计角度更安全:不需要 unwrap,不能为 nil,不隐藏错误。如果对象不能没有父节点而存在,unowned 使其成为明确的契约,而 weak 模糊了保证。
有:unowned 更快,因为它不需要访问运行时 weak 表进行归零。在大多数应用中差异不明显,但在有数百万次访问的高负载场景中,unowned 在读取时可能快 10–20%。
重构——unowned 的主要危险。对象生命周期的变化(缓存、异步操作、重用)可能违反保证。编译器不会警告。解决方案:在架构变更时迁移到 weak 或添加警告注释。
总结
unowned let 或 unowned var;可以是非可选和可选(Swift 5.0+)我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。