Swift中A强引用B、B弱引用A时为何A总是比B先释放?
核心原因
你观察到的A实例永远先于B实例释放的现象,本质是强引用的持有逻辑导致的,B实例的生命周期完全被A实例的强引用绑定,只要A实例没有被销毁,B实例的引用计数就不可能降到0,自然不可能先于A触发deinit。
错误认知纠正
你认为bravo = nil会让alpha.b同步变为nil的认知是错误的:只有weak/unowned修饰的弱引用变量,才会在指向的实例被释放时自动置空;而A类的b属性是普通强引用,不会因为其他指向B实例的变量被置空就自动变为nil,它会一直持有B实例的强引用,直到A实例本身被销毁。
各阶段引用计数变化明细
初始状态
alpha、bravo均为nil,无实例存在。
执行setup函数阶段
- 执行
b = B():因为是inout参数,相当于给外部的bravo赋值B实例,此时B实例强引用计数=1,持有者为bravo - 执行
a = A(b: b!):相当于给外部的alpha赋值A实例,A初始化时强持有传入的B实例,此时:- A实例强引用计数=1,持有者为
alpha - B实例强引用计数+1 → 变为2,新增持有者为
A实例的b属性
- A实例强引用计数=1,持有者为
- 执行
b?.a = a:B的a属性是weak修饰的弱引用,赋值不会增加A的引用计数,两个实例的引用计数无变化 - setup函数执行完毕,局部参数释放,不影响实例计数,此时持有关系为:
alpha 强持有 → A实例 强持有 → B实例 bravo 强持有 → B实例 B实例 weak持有 → A实例
赋值nil阶段,两种顺序的表现一致
顺序1:先执行bravo = nil,再执行alpha = nil
bravo = nil:解除bravo对B实例的强引用,B的强引用计数-1 → 变为1,剩余A实例的b属性持有B,B不会释放alpha = nil:解除alpha对A实例的强引用,A的强引用计数变为0,触发A的deinit,打印Destroying A;A被销毁后,它持有的b属性强引用也同步解除,B的强引用计数变为0,触发B的deinit,打印Destroying B
顺序2:先执行alpha = nil,再执行bravo = nil
alpha = nil:解除alpha对A实例的强引用,A的强引用计数变为0,触发A的deinit,打印Destroying A;A被销毁后,它持有的b属性强引用同步解除,B的强引用计数-1 → 变为1,剩余bravo持有B,B不会释放bravo = nil:解除bravo对B实例的强引用,B的强引用计数变为0,触发B的deinit,打印Destroying B
两种顺序下都是A先释放、B后释放,和你测试的结果完全吻合。
内容的提问来源于stack exchange,提问作者theevo
相关产品推荐
相关产品推荐

