Swift 与 Objective-C

Swift ARC:验证循环引用与 weak、unowned 的生命周期

建立并切断强引用循环,观察weak变成nil,并通过可选的unowned引用区分手动清除与自动置空的差异。

2 分钟阅读
Swift ARC:验证循环引用与 weak、unowned 的生命周期 封面图

weak与unowned都不拥有目标,但释放后的行为不同。weak会变成nil;通过安全的unowned访问已释放目标则会触发运行时错误。选择依据是生命周期保证,而不只是可选类型语法。

原理与设计判断

ARC结合编译器生成的所有权操作与运行时的引用管理。强引用循环中的对象可能在外部拥有者消失后仍然存在。unowned也可以是可选引用,但开发者必须确保它指向存活对象,或明确设置为nil。 一手资料

复现代码

第一个实验让两个Node相互拥有,移除外部强引用后通过weak观察存活,再切断一条连接。第二个让父对象强引用子对象、子对象以weak反向引用。最后在Owner仍然存活时,手动清除可选的unowned引用。

这个独立示例已于 2026-10-23 使用 Apple Swift 6.4,以 Swift 6 语言模式在 arm64 macOS 目标环境运行。不需要网络或额外软件包。

下载完整 Swift 文件

请在下载文件的文件夹中运行。检查不通过时,precondition 会终止程序。请勿使用 -Ounchecked,因为它会移除这些检查。

swift -swift-version 6 swift-arc-weak-unowned.swift
final class Node { var next: Node? }
func breakCycle(_ node: Node) { node.next = nil }

weak var observedA: Node?
weak var observedB: Node?
do {
    let a = Node()
    let b = Node()
    a.next = b
    b.next = a
    observedA = a
    observedB = b
}
precondition(observedA != nil && observedB != nil)
breakCycle(observedA!)
precondition(observedA == nil && observedB == nil)

final class Owner { var child: Child? }
final class Child { weak var owner: Owner? }
weak var observedOwner: Owner?
weak var observedChild: Child?
do {
    let owner = Owner()
    let child = Child()
    owner.child = child
    child.owner = owner
    observedOwner = owner
    observedChild = child
}
precondition(observedOwner == nil && observedChild == nil)

final class Borrower {
    unowned var owner: Owner?
    init(_ owner: Owner) { self.owner = owner }
}
do {
    let owner = Owner()
    let borrower = Borrower(owner)
    withExtendedLifetime(owner) {
        precondition(borrower.owner === owner)
        borrower.owner = nil
        precondition(borrower.owner == nil)
    }
}
print("cycle=retained broken=released weak=nil unowned-optional=manually-cleared")

运行结果与解读

cycle=retained broken=released weak=nil unowned-optional=manually-cleared

强引用循环在代码块结束后仍存在,切断一条连接后两个观察值才变成nil。使用weak反向引用的父子关系在强引用消失后释放。unowned可选引用的nil来自主动赋值,不是运行时自动置空。

适用范围与限制

本程序不访问已失效的unowned,也没有声称实测那种崩溃。不能保证对象一定在特定源码行释放,优化与生命周期延长都会影响。缺少deinit日志也不能直接证明泄漏,缓存或异步工作可能合法持有对象。

接下来要确认的事

先画所有权图,判断反向关系是否需要拥有目标。允许目标先消失时使用weak;访问期间保证存活才考虑unowned。如果问题来自存储的闭包持有自身对象,可接着看捕获实验。

延伸实验

来源与验证