我的印象是,我的
CALayer
在被添加为子层后被保留,直到结束执行块,而不是直到 I 停止引用它。
然而,一旦按预期取消引用,父 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
仍然被引用。
如果我现在在取消引用实例后立即退出主线程,则内存将被正确释放:
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
根据这些观察,我的猜测是,调用
addSublayer
对层有保留副作用,该副作用会持续到主线程上当前执行块结束。我很惊讶地看到这一点,并且很好奇到底发生了什么。
注意:在 Playground 中运行相同的代码会产生另一个结果......
根据 Sweeper 的建议,我从图层取消初始化时运行
thread backtrace
:
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
* frame #0: 0x00000001029267ec TestCALayerRelease`DebugCALayer.__deallocating_deinit(self=0x0000600000c64d50) at DebugViews.swift:20:11
frame #1: 0x00000001029269dc TestCALayerRelease`@objc DebugCALayer.__deallocating_deinit at <compiler-generated>:0
frame #2: 0x000000018a122644 QuartzCore`CA::release_objects(X::List<void const*>*) + 28
frame #3: 0x000000018a122d84 QuartzCore`CA::Transaction::commit() + 1384
frame #4: 0x000000018a123f7c QuartzCore`CA::Transaction::flush_as_runloop_observer(bool) + 68
frame #5: 0x000000018040de34 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
frame #6: 0x0000000180408838 CoreFoundation`__CFRunLoopDoObservers + 528
frame #7: 0x0000000180408cf0 CoreFoundation`__CFRunLoopRun + 968
frame #8: 0x0000000180408514 CoreFoundation`CFRunLoopRunSpecific + 572
...
这确认了
QuartzCore
保留该层直到当前运行循环结束。