为何停止引用CALayer后它仍被保留?
CALayer 延迟释放的原因解析
现象说明
将CALayer添加为子图层后,它不会在取消引用时立即释放,而是会被保留到当前执行块结束;但父UIView在取消引用后会按预期立即释放。
调试用代码
class DebugCALayer: CALayer { let id: String init(_ id: String) { self.id = id print("DebugCALayer init \(id)") super.init() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit { print("DebugCALayer deinit \(id)") } } class DebugUIView: UIView { private let id: String init(_ id: String) { print("DebugUIView init \(id)") self.id = id super.init(frame: .zero) layer.addSublayer(DebugCALayer(id)) } required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } deinit { print("DebugUIView deinit \(id)") } }
基础测试与结果
测试代码
final class CALayerReleaseTests: XCTestCase { override func setUp() async throws { print("setup") } override func tearDown() { print("tear down") } func testBaseline() throws { for i in 0..<2 { _ = DebugUIView("\(i)") } } }
日志输出
setup init DebugUIView 0 init DebugCALayer 0 deinit DebugUIView 0 init DebugUIView 1 init DebugCALayer 1 deinit DebugUIView 1 tear down deinit DebugCALayer 1 deinit DebugCALayer 0
可以看到:UIView实例在取消引用后立即销毁,但CALayer要等到测试执行完毕(tear down之后)才被释放。
异步场景下的正常释放
如果在取消引用实例后,触发一次主线程异步任务,CALayer就能按预期立即释放:
修改后的测试代码
func testDispatch() throws { for i in 0..<2 { _ = DebugUIView("\(i)") let exp = expectation(description: "") DispatchQueue.main.async { exp.fulfill() } waitForExpectations(timeout: 1) } }
日志输出
setup init DebugUIView 0 init DebugCALayer 0 deinit DebugUIView 0 deinit DebugCALayer 0 init DebugUIView 1 init DebugCALayer 1 deinit DebugUIView 1 deinit DebugCALayer 1 tear down
核心原因
这一现象由Core Animation的事务机制导致:
- Core Animation会将所有图层操作(比如
addSublayer)打包到一个事务中,默认在主线程RunLoop的下一个周期结束时提交事务。 - 事务提交前,Core Animation内部会临时持有这些图层的引用,防止图层在事务处理完成前被释放,这就导致
CALayer的释放被延迟到当前RunLoop循环结束(即当前执行块完成后)。 UIView的生命周期由ARC直接管理,没有被Core Animation的事务机制额外持有,因此能在取消引用后立即释放。- 使用
DispatchQueue.main.async时,会触发一次RunLoop循环,事务被提交,Core Animation的临时引用被释放,CALayer也就随之被ARC销毁。
注:在Playground中运行相同代码会得到不同结果,因为Playground的RunLoop运行机制和正式测试环境存在差异。
内容的提问来源于stack exchange,提问作者Guig
相关产品推荐
相关产品推荐

