【问题标题】:iPhone UI addSubview causing concurrency exceptioniPhone UI addSubview 导致并发异常
【发布时间】:2025-12-13 10:50:01
【问题描述】:

这真的很奇怪......

我运行我的应用程序,当它打开并且正在构建视图时,我得到:

Collection <CALayerArray: 0x124650> was mutated while being enumerated.

代码跟踪如下:

main
UIApplicationMain
-[UIApplication _run]
CFRunLoopRunInMode
CFRunLoopRunSpecific
_UIApplicationHandleEvent
-[UIApplication sendEvent:]
-[UIApplication handleEvent:withNewEvent:]
-[UIApplication _runWithURL:sourceBundleID:]
-[UIApplication _performInitilizationWithURL:sourceBundleID:]
-[AppDelegate applicationDidFinishLaunching:]
+[Controller initializeController] //This is my own function
    [window addSubview: pauseMenuController.view] //This is the last point of my code it goes through
-[UIView(Hierarchy) addSubview:]
-[UIView(Internal) _addSubview:positioned:relativeTo:]
-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:]
-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]
-[UIView(Hierarchy) _makeSubtreePerformSelector:withObject:withObject:copySublayers:]
_NSFastEnumerationMutationHandler
objc_exception_throw

我已经运行了很多次游戏,但我从未见过这个,然后它突然出现了。奇怪的是,我没有创建任何其他线程(我知道),直到 after 这段代码都被调用。如果有人可以给我一些解释,说明在 UIView 中访问它时可能会被修改的内容,我会更容易调试它。它是否与在视图中添加一些东西有关,而它已经添加了一些东西,也许?有什么想法吗?

【问题讨论】:

  • 您能否详细说明出现错误时应用行为的不同之处?崩溃……没有视图……等等……
  • 你能发布一些代码吗?调用-addSubview:这个简单的行为不会导致这个问题,没有代码我们只能盲目猜测。

标签: iphone objective-c nsexception


【解决方案1】:

您的视图是否可能有一个子层,您将委托分配为视图?这通常会导致视图在调用_makeSubtreePerformSelector:withObject:withObject:copySublayers: 时无限递归(嗯,直到它达到堆栈限制),但我想它在这里尝试做的任何事情都可能涉及突变。

之所以如此,是因为 UIView 假设如果 CALayer 的委托是 UIView,则 CALayer 属于 UIView,而 UIView 是层次结构的一部分。但是,如果您创建自己的 CALayer 并将委托指定为 UIView,则 UIView 最终将作为递归的一部分调用自身。

【讨论】:

  • 绝对是一个很好的回应,尽管如果你说的是这种情况,那么每次都会发生这种情况。事实上,它在数百或数千次运行中只发生一次。嗯。
  • Kevin 对 UIView 层次结构 re delegates 的解释帮助我解决了一个切题问题:我添加了一个 CALayer 作为视图层的子层,并将控制器分配为委托。尽管在 viewDidUnload/dealloc 我在子层之前取消/释放了视图,但我在_makeSubtreePerform...copySublayers 上崩溃了。解决方案是在 取消任何内容之前删除子层。大概这会阻止作为委托的控制器调用已发布的子层。
【解决方案2】:

来自文档:

枚举是“安全的”——枚举器有一个突变保护,因此如果您尝试在枚举期间修改集合,则会引发异常。

基本上,这意味着当您使用 Objective-C 2.0 中引入的快速枚举来枚举它时,不允许您向集合(例如数组)添加/删除对象。因为这样做会使突变保护无效。

在您的情况下,集合与视图层次结构相关。如果您想向此特定集合添加和/或删除视图,请不要使用快速枚举。

【讨论】:

  • 我知道第一部分,快速枚举发生在苹果这边,而不是我这边。
【解决方案3】:

当您尝试在 CALayer 的 -dealloc 方法期间清除一个属性时,我已经看到在 CALayers 中发生这种情况,只是更改该属性会无意中更改图层的可见属性。您可以检查并查看您是否以某种方式在同一视图的 -dealloc 期间以某种方式更改了您的一个视图的属性。

【讨论】:

    【解决方案4】:

    这不是并发异常。当某些东西(可能在同一线程中的循环内)更改循环正在迭代的数组/集合/字典时,会发生枚举突变异常。

    由于 addSubview: 在堆栈上,我的猜测是在构建过程中有些东西试图删除 UIView 的子视图之一。

    您是否覆盖了任何可能正在运行的方法?像 addSubview 或可能的 layoutSubviews?如果其中任何一个正在删除子视图,那么这将导致问题。

    【讨论】:

    • 太棒了,这就是问题所在,谢谢!我认为这是一个并发问题,因为通常为了在访问时发生变异,您需要多个线程,但您是对的,情况并非总是如此。
    • 完全准确。我有同样的问题。在我的情况下,添加的视图的 viewController 实际上弄乱了超级视图的布局。非常感谢。您为我节省了数小时的时间来解决这个问题。
    【解决方案5】:

    我遇到了同样的错误。我在非主线程上改变了视图层次结构。确保对视图的所有修改都在主线程上执行。

    【讨论】:

      最近更新 更多