为什么当我停止引用 CALayer 后它仍然保留?

问题描述 投票:0回答:1

我的印象是,我的

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 中运行相同的代码会产生另一个结果......

ios swift memory-management core-animation automatic-ref-counting
1个回答
0
投票

根据 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
保留该层直到当前运行循环结束。

© www.soinside.com 2019 - 2024. All rights reserved.