设置CAMetalLayer.presentsWithTransaction=true后后台线程崩溃问题
问题场景
当设置metalLayer.presentsWithTransaction = true,且在后台渲染线程调用nextDrawable.present()时,应用偶尔崩溃,抛出如下异常:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
预期结果:应用不会崩溃,渲染可正常进行。
崩溃堆栈信息
Fatal Exception: NSInternalInconsistencyException Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread. Fatal Exception: NSInternalInconsistencyException 0 CoreFoundation 0x1aa30a5ac __exceptionPreprocess 1 libobjc.A.dylib 0x1be38442c objc_exception_throw 2 CoreAutoLayout 0x1be60ad4c -[NSISEngine tryToOptimizeReturningMutuallyExclusiveConstraints] 3 CoreAutoLayout 0x1be60aa20 -[NSISEngine _optimizeWithoutRebuilding] 4 CoreAutoLayout 0x1be60a4f8 -[NSISEngine optimize] 5 CoreAutoLayout 0x1be605b34 -[NSISEngine performPendingChangeNotifications] 6 UIKitCore 0x1ad13eef4 -[UIView(Hierarchy) layoutSubviews] 7 UIKitCore 0x1ad151dd4 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] 8 QuartzCore 0x1ad65b280 -[CALayer layoutSublayers] 9 QuartzCore 0x1ad65b73c CA::Layer::layout_if_needed(CA::Transaction*) 10 QuartzCore 0x1ad66fb64 CA::Layer::layout_and_display_if_needed(CA::Transaction*) 11 QuartzCore 0x1ad5b7114 CA::Context::commit_transaction(CA::Transaction*, double, double*) 12 QuartzCore 0x1ad5e2418 CA::Transaction::commit() 13 Flutter 0x1032e4778 (Missing) 14 Flutter 0x1035b85ec (Missing) 15 Flutter 0x1035b9094 (Missing) 16 Flutter 0x1035b88b0 (Missing) 17 Flutter 0x1035bedb0 (Missing) 18 Flutter 0x1032ef46c (Missing) 19 Flutter 0x1032f13e0 (Missing) 20 CoreFoundation 0x1aa288050 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ 21 CoreFoundation 0x1aa287c50 __CFRunLoopDoTimer 22 CoreFoundation 0x1aa2870c4 __CFRunLoopDoTimers 23 CoreFoundation 0x1aa281178 __CFRunLoopRun 24 CoreFoundation 0x1aa2804bc CFRunLoopRunSpecific 25 Flutter 0x1032f12bc (Missing) 26 Flutter 0x1032f0c44 (Missing) 27 libsystem_pthread.dylib 0x1f185aca8 _pthread_start 28 libsystem_pthread.dylib 0x1f1863788 thread_start
解决方案
将present()移至主线程执行:
当presentsWithTransaction = true时,present()操作会触发CA事务提交,而该过程可能间接触发UIKit布局引擎的操作,这类操作必须在主线程完成。可以将present调用包装到主线程队列中:DispatchQueue.main.async { drawable.present() }关闭presentsWithTransaction:
如果业务场景不需要依赖CA事务的提交时机,直接设置metalLayer.presentsWithTransaction = false,这样present操作不会绑定到CA事务,避免触发主线程相关的布局逻辑。注意该调整可能影响渲染同步时机,需测试验证是否符合业务需求。确保所有布局操作仅在主线程执行:
排查代码中是否存在后台线程触发UI布局的逻辑,比如修改UIView的frame、约束等操作必须严格放在主线程处理,即使是Metal渲染相关的UI层调整也不例外。
内容的提问来源于stack exchange,提问作者luckysmg

