I’ve the impression that my CALayer
is retained after being added as a sublayer till the tip execution block, as a substitute of till I cease referencing it.
The father or mother UIView is nevertheless launched as quickly as it’s de-referenced as anticipated.
class DebugCALayer: CALayer {
let id: String
init(_ id: String) {
self.id = id
print("DebugCALayer init (id)")
tremendous.init()
}
required init?(coder: NSCoder) { fatalError("init(coder:) has not been applied") }
deinit { print("DebugCALayer deinit (id)") }
}
class DebugUIView: UIView {
non-public let id: String
init(_ id: String) {
print("DebugUIView init (id)")
self.id = id
tremendous.init(body: .zero)
layer.addSublayer(DebugCALayer(id))
}
required init?(coder _: NSCoder) { fatalError("init(coder:) has not been applied") }
deinit { print("DebugUIView deinit (id)") }
}
This may be examined with this code:
remaining 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)")
}
}
}
which logs
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
The discharge cycle for the UIView
is as anticipated, with cases being de-inititalized as quickly as I’m not referencing them.
Nonetheless the CALayer
s stay referenced till after the check is teared-down.
If I now exit the principle thread proper after de-referencing my occasion, the reminiscence is deallocated appropriately:
func testDispatch() throws {
for i in 0..<2 {
_ = DebugUIView("(i)")
let exp = expectation(description: "")
DispatchQueue.essential.async {
exp.fulfill()
}
waitForExpectations(timeout: 1)
}
}
logs
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
My guess from these observations is that calling addSublayer
has a retaining facet impact on the layer that lasts till the tip of the present execution block on the principle thread. I used to be stunned to see this and was curious to grasp what is precisely taking place.
Observe: operating the identical code within the Playground offers yet one more end result…