Unowned Reference:是什么、语法及在移动应用中的应用

作者: IT Sectr 发布日期: 2026-03-30 阅读时间: 9 分钟

Unowned Reference(无主引用)——是 Swift 中一种不持有的引用,它不增加对象的 retain count,与 weak 不同,它在对象释放后不会设置为 nil。根据 Apple Swift Language Guide, 2026,当确保对象至少与引用它的对象存活时间相同时使用 unowned。与 Weak Reference 不同,unowned 不需要 unwrap——它是非可选类型,使代码更简洁,但将生命周期保证的责任交给了开发者。

要点

  • Unowned Reference——不带自动归零的不持有引用;非可选类型,不增加 retain count
  • 保证——当对象保证不能在引用它的对象之前被释放时使用
  • 与 weak 的区别——unowned 不会归零为 nil(崩溃风险),weak 会归零(安全)
  • 场景——具有生命周期保证的 parent-child、带有 unowned self 的闭包、单例和 Service Locator
  • 风险——访问已释放的 unowned 对象会导致运行时崩溃(EXC_BAD_ACCESS)

什么是 Unowned Reference?

Unowned Reference——是 ARC 中对对象的一种不持有引用,不增加其 retain count。与 weak 不同,unowned 在对象释放后不会归零:它继续指向已经释放的内存区域。访问这样的引用会导致 EXC_BAD_ACCESS 运行时崩溃。

“无主”一词反映了语义:对象存在,但没有人对其生命周期负责。开发者明确声明:“我保证只要我引用它,这个对象就会存活”。编译器不检查这个保证——这是开发者层面的契约

根据 Swift.org Documentation, 2026,在保证生命周期的场景中,unowned 引用比 weak 更受欢迎,因为:它们不需要可选类型(更干净的代码),不需要 unwrap(更少的 force-unwrap 或 guard let),并且没有维护归零 weak 表的开销。然而,任何违反契约的行为——都会崩溃。

Swift 中 unowned 的语法

在 Swift 中,unowned 引用使用关键字 unowned 在 let 或 var 之前声明。与 weak 不同,unowned 可以是 let 和 var,并且不需要可选类型。这个特性使 unowned 对于根据领域逻辑不能为 nil 的引用很方便。

swift
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

unowned var 是允许的但不太常见。当引用可能被替换时使用(例如,将子节点重新连接到不同的父节点)。替换时,旧对象的释放是外部所有者的责任。

Unowned Optional

Swift 5.0+ 中添加了对 unowned optional 的支持(unowned let x: Type?)。这是一种折衷:unowned 保证如果引用不是 nil,则对象存活。释放时的行为——崩溃,与普通的 unowned 相同。

Unowned vs Weak:何时使用什么

在 unowned 和 weak 之间选择——设计 Swift 架构时常见的决策之一。让我们看看每种情况的标准和建议

标准WeakUnowned
Optional是(Type?)否(Type)
释放时归零自动为 nil否(悬空指针风险)
类型(let/var)仅 varlet 或 var
性能weak 表的开销最小(简单指针)
安全性安全(nil 被检查)EXC_BAD_ACCESS 风险
生命周期保证不需要需要明确保证

实用规则

如果对对象的生命周期有任何疑问,使用 weak。Weak 安全、易懂且不需要证明。仅当排除了对象可能提前释放的所有场景时,才使用 unowned。典型情况:没有父节点就不存在的子节点;同步执行的闭包;在其初始化器内引用对象。

根据 Airbnb Swift Style Guide, 2025,在大型代码库中建议默认使用 weak,而 unowned——仅附有解释生命周期保证的明确注释。这降低了重构时意外崩溃的风险。

闭包中的 Unowned self

闭包——在 parent-child 关系之后第二常见的 unowned 使用场景。当确保 self 比闭包存活得更久时,使用捕获列表 [unowned self]。让我们看看正确和错误的场景。

何时 unowned self 安全

同步闭包——sorted、filter、map。它们立即在当前线程中执行,self 肯定存活。这里使用 unowned 的捕获列表是可以接受的,并且提供了更干净的代码。

swift
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 }
}

何时 unowned self 危险

异步闭包——带有延迟、网络请求、动画。Self 可能在闭包放置和执行之间被释放。这里 unowned self → 崩溃。使用 [weak self]

swift
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。为每个 unowned 添加注释:为什么这个引用是安全的以及在什么条件下可能被违反。

UIKit 层级中的 Unowned

UIKit——unowned 的高风险区域。ViewController 在导航(pop、dismiss)、从内存卸载、方向更改时随时可能被释放。如果您将 ViewController 传递给带有 unowned self 的闭包——从后台返回或动画结束时,self 可能为 nil。

最佳实践

为减少使用 unowned 时的风险,请遵循以下规则:

  • 默认优先选择 weak——weak 安全,unowned 是优化,不是标准
  • 记录保证——为每个 unowned 编写带有理由的注释
  • 避免在 ViewController 中使用 unowned——UIKit 生命周期对 unowned 保证来说不可预测
  • 仅对同步闭包使用 unowned——sorted、filter、map——安全的候选者
  • 在代码审查中检查——每个 unowned 都需要代码作者的理由
  • 一旦有疑问就迁移到 weak——可读性的损失(一个 guard let)小于生产中的崩溃
swift
// 示例:带有明确理由的已记录 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,以及什么条件可能破坏保证。

常见问题

访问已释放对象的 unowned 引用时会发生什么?

运行时崩溃,EXC_BAD_ACCESS。Swift 在访问时不检查 unowned 引用的有效性——它只是一个“原始”指针。如果对象已被释放,内存被覆盖,访问它会导致崩溃。这是一个不可捕获的异常(不是 try-catch)。

Unowned 可以与协议一起使用吗?

可以,如果协议继承自 AnyObject。Unowned 适用于所有引用类型:类、AnyObject 协议、Objective-C 对象。值类型(struct、enum)不支持 unowned,因为它们不参与 ARC。

什么时候 unowned 比 weak 更安全?

当生命周期保证是绝对且明显的时候——unowned 从设计角度更安全:不需要 unwrap,不能为 nil,不隐藏错误。如果对象不能没有父节点而存在,unowned 使其成为明确的契约,而 weak 模糊了保证。

Unowned 和 weak 之间有性能差异吗?

有:unowned 更快,因为它不需要访问运行时 weak 表进行归零。在大多数应用中差异不明显,但在有数百万次访问的高负载场景中,unowned 在读取时可能快 10–20%。

重构如何影响 unowned 保证?

重构——unowned 的主要危险。对象生命周期的变化(缓存、异步操作、重用)可能违反保证。编译器不会警告。解决方案:在架构变更时迁移到 weak 或添加警告注释。

总结

  • Unowned Reference——不带归零的不持有引用;非可选类型,不增加 retain count
  • 保证——需要明确证据表明对象至少与引用它的代码存活时间相同
  • 语法——unowned letunowned var;可以是非可选和可选(Swift 5.0+)
  • Unowned vs Weak——unowned 更快更简洁,但 weak 更安全;weak——默认选择
  • 闭包——unowned self 仅用于同步闭包;异步需要 [weak self]
  • 文档——每个 unowned 都应有带有保证理由的注释
  • 建议——有疑问时选择 weak;unowned——用于明确和有记录的契约

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读