Sync 解决了 Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: > was mutated while being enumerated
Sync solves Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: > was mutated while being enumerated
我相信我已经找到了错误的解决方案,但不明白我是否只是通过降低代码速度来避免我的解决方案出现竞争条件,或者我是否真的在解决问题。
我的问题是2部分
1) 为什么将 dispatch.main.async
更改为 dispatch.main.sync
会使错误消失,因为我已经正确编码或者我只是暂时解决了这个问题?
2) 我是否需要将所有 dispatch.main.async
更改为 dispatch.main.sync
以用于 2 个额外的调用?
Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x600002a24180> was mutated while being enumerated.
在这份报告中,第二个答案特别指出,永远不要使用 dispatch.main.sync
,因为它会导致死锁。第一个答案说,你可能想使用同步,但它会导致死锁。
苹果报错如下:
Thread 5 name: Dispatch queue: com.apple.cloudkit.operation-7BCFBC195B4B17C3.callback
Thread 5 Crashed:
0 libsystem_kernel.dylib 0x00000001b4e120dc 0x1b4def000 + 143580
1 libsystem_pthread.dylib 0x00000001b4e8b094 0x1b4e89000 + 8340
2 libsystem_c.dylib 0x00000001b4d6af4c 0x1b4d10000 + 372556
3 libsystem_c.dylib 0x00000001b4d6aeb4 0x1b4d10000 + 372404
4 libc++abi.dylib 0x00000001b4437788 0x1b4436000 + 6024
5 libc++abi.dylib 0x00000001b4437934 0x1b4436000 + 6452
6 libobjc.A.dylib 0x00000001b444ee00 0x1b4449000 + 24064
7 libc++abi.dylib 0x00000001b4443838 0x1b4436000 + 55352
8 libc++abi.dylib 0x00000001b44438c4 0x1b4436000 + 55492
9 libdispatch.dylib 0x00000001b4cb47e8 0x1b4c54000 + 395240
10 libdispatch.dylib 0x00000001b4c5d324 0x1b4c54000 + 37668
11 libdispatch.dylib 0x00000001b4c5de74 0x1b4c54000 + 40564
12 libdispatch.dylib 0x00000001b4c664ac 0x1b4c54000 + 74924
13 libsystem_pthread.dylib 0x00000001b4e95114 0x1b4e89000 + 49428
14 libsystem_pthread.dylib 0x00000001b4e97cd4 0x1b4e89000 + 60628
当我 运行 Xcode 中的代码时,我可以重现 Xcode 中的错误并获得以下堆栈跟踪。
2019-07-31 19:51:07.893799-0400 My Toy Chest[49278:1080065] *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x600002a24180> was mutated while being enumerated.'
*** First throw call stack:
(
0 CoreFoundation 0x00000001106d38db __exceptionPreprocess + 331
1 libobjc.A.dylib 0x000000010f6f4ac5 objc_exception_throw + 48
2 CoreFoundation 0x00000001106d07ac __NSFastEnumerationMutationHandler + 124
3 CoreData 0x0000000110048dd4 -[NSManagedObjectContext executeFetchRequest:error:] + 3332
4 My Toy Chest 0x0000000109248bc0 $s12My_Toy_Chest21ActionFigureSpecificsC03setdeF8CoreDataSbyF + 1472
5 My Toy Chest 0x000000010923a4b2 $s12My_Toy_Chest21ActionFigureSpecificsC03setdeF0yyF + 546
6 My Toy Chest 0x0000000109313c6b $s12My_Toy_Chest22SharedDataActionFigureC03getfg26SpecificsICloudForCompletefG5ArrayyyFySaySo8CKRecordCGSg_s5Error_pSgtcfU_ + 3051
7 My Toy Chest 0x000000010931409c $s12My_Toy_Chest22SharedDataActionFigureC03getfg26SpecificsICloudForCompletefG5ArrayyyFySaySo8CKRecordCGSg_s5Error_pSgtcfU_TA + 12
8 My Toy Chest 0x0000000109315233 $s12My_Toy_Chest22SharedDataActionFigureC013fetchCompletefG18SpecificsFromCloud10completionyySaySo8CKRecordCGSg_s5Error_pSgtc_tFySo13CKQueryCursorCSg_AKtcfU0_ + 1331
9 My Toy Chest 0x0000000109315e01 $s12My_Toy_Chest22SharedDataActionFigureC013fetchCompletefG18SpecificsFromCloud10completionyySaySo8CKRecordCGSg_s5Error_pSgtc_tFySo13CKQueryCursorCSg_AKtcfU0_TA + 49
10 My Toy Chest 0x00000001092373b4 $sSo13CKQueryCursorCSgs5Error_pSgIeggg_ACSo7NSErrorCSgIeyByy_TR + 132
11 CloudKit 0x000000010f51e84d -[CKQueryOperation _finishOnCallbackQueueWithError:] + 613
12 CloudKit 0x000000010f51795a -[CKOperation _finishInternalOnCallbackQueueWithError:] + 582
13 CloudKit 0x000000010f5176fa -[CKOperation _handleCompletionCallback:] + 148
14 CloudKit 0x000000010f51e521 -[CKQueryOperation _handleCompletionCallback:] + 197
15 CloudKit 0x000000010f58d438 __82-[CKOperationCallbackManager _performCallbackForOperation:callback:errorCallback:]_block_invoke + 288
16 libdispatch.dylib 0x000000011189e725 _dispatch_block_async_invoke2 + 83
17 libdispatch.dylib 0x0000000111890db5 _dispatch_client_callout + 8
18 libdispatch.dylib 0x0000000111898225 _dispatch_lane_serial_drain + 778
19 libdispatch.dylib 0x0000000111898ed0 _dispatch_lane_invoke + 477
20 libdispatch.dylib 0x00000001118a2ea3 _dispatch_workloop_worker_thread + 733
21 libsystem_pthread.dylib 0x0000000111c79611 _pthread_wqthread + 421
22 libsystem_pthread.dylib 0x0000000111c793fd start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException
我很确定发生缺陷的代码是以下一组代码,它更新了 View Controller 集合视图中的 UI。
以下代码崩溃(异步)
DispatchQueue.main.async {
self.delegate?.updateActionFigureSpecificsModel()
}
以下代码不会崩溃(同步)
DispatchQueue.main.sync {
self.delegate?.updateActionFigureSpecificsModel()
}
更新视图控制器集合视图的后续调用
func updateActionFigureSpecificsModel() {
DispatchQueue.main.async {
self.delegate?.updateActionFigureModel(forActionFigure: self, forDelegateProperty: .specifics)
}
}
func updateActionFigureModel(forActionFigure actionFigure: ActionFigure, forDelegateProperty delegateProperty: DelegateProperty) {
switch delegateProperty {
case .specifics, .eBaySaleSummary:
DispatchQueue.main.async {
self.setUIActionFigure(forActionFigure: actionFigure)
}
}
}
func setUIActionFigure(forActionFigure actionFigure: ActionFigure) {
if let indexPath = getIndexPath(forActionFigure: actionFigure) {
DispatchQueue.main.async {
self.collectionView.reloadItems(at: [indexPath])
}
}
我真的无法从你发布的内容中判断出确切的问题是什么,但你可能 运行正在处理以下一个或两个一般问题:
- 基础 collection 不是 thread-safe 并且不能从多个线程修改。
- 基础 collection 在迭代时无法修改。
我怀疑 collection 你 using/iterating/modifying 在你正在 运行 的任何线程中与你正在完成的任务 use/iteration/modification 发生冲突正在 main
调度。是的,我怀疑将 async
更改为 sync
只是通过等待 main
任务在后台线程恢复之前完成来回避问题。 通常,如果后台线程和主线程共享对同一个基础的引用collection——并且它们中的任何一个都修改了那个collection——你最终会运行 遇到问题...除非你非常非常小心。
我经常使用的一个解决方案是将我的 collection 分开。我的后台线程将进行更新或执行更新 collection 的计算。然后我将复制 collection 并将该副本传递给 main
以供显示和 UI 交互。
我相信我已经找到了错误的解决方案,但不明白我是否只是通过降低代码速度来避免我的解决方案出现竞争条件,或者我是否真的在解决问题。
我的问题是2部分
1) 为什么将 dispatch.main.async
更改为 dispatch.main.sync
会使错误消失,因为我已经正确编码或者我只是暂时解决了这个问题?
2) 我是否需要将所有 dispatch.main.async
更改为 dispatch.main.sync
以用于 2 个额外的调用?
Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x600002a24180> was mutated while being enumerated.
在这份报告中,第二个答案特别指出,永远不要使用 dispatch.main.sync
,因为它会导致死锁。第一个答案说,你可能想使用同步,但它会导致死锁。
苹果报错如下:
Thread 5 name: Dispatch queue: com.apple.cloudkit.operation-7BCFBC195B4B17C3.callback
Thread 5 Crashed:
0 libsystem_kernel.dylib 0x00000001b4e120dc 0x1b4def000 + 143580
1 libsystem_pthread.dylib 0x00000001b4e8b094 0x1b4e89000 + 8340
2 libsystem_c.dylib 0x00000001b4d6af4c 0x1b4d10000 + 372556
3 libsystem_c.dylib 0x00000001b4d6aeb4 0x1b4d10000 + 372404
4 libc++abi.dylib 0x00000001b4437788 0x1b4436000 + 6024
5 libc++abi.dylib 0x00000001b4437934 0x1b4436000 + 6452
6 libobjc.A.dylib 0x00000001b444ee00 0x1b4449000 + 24064
7 libc++abi.dylib 0x00000001b4443838 0x1b4436000 + 55352
8 libc++abi.dylib 0x00000001b44438c4 0x1b4436000 + 55492
9 libdispatch.dylib 0x00000001b4cb47e8 0x1b4c54000 + 395240
10 libdispatch.dylib 0x00000001b4c5d324 0x1b4c54000 + 37668
11 libdispatch.dylib 0x00000001b4c5de74 0x1b4c54000 + 40564
12 libdispatch.dylib 0x00000001b4c664ac 0x1b4c54000 + 74924
13 libsystem_pthread.dylib 0x00000001b4e95114 0x1b4e89000 + 49428
14 libsystem_pthread.dylib 0x00000001b4e97cd4 0x1b4e89000 + 60628
当我 运行 Xcode 中的代码时,我可以重现 Xcode 中的错误并获得以下堆栈跟踪。
2019-07-31 19:51:07.893799-0400 My Toy Chest[49278:1080065] *** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x600002a24180> was mutated while being enumerated.'
*** First throw call stack:
(
0 CoreFoundation 0x00000001106d38db __exceptionPreprocess + 331
1 libobjc.A.dylib 0x000000010f6f4ac5 objc_exception_throw + 48
2 CoreFoundation 0x00000001106d07ac __NSFastEnumerationMutationHandler + 124
3 CoreData 0x0000000110048dd4 -[NSManagedObjectContext executeFetchRequest:error:] + 3332
4 My Toy Chest 0x0000000109248bc0 $s12My_Toy_Chest21ActionFigureSpecificsC03setdeF8CoreDataSbyF + 1472
5 My Toy Chest 0x000000010923a4b2 $s12My_Toy_Chest21ActionFigureSpecificsC03setdeF0yyF + 546
6 My Toy Chest 0x0000000109313c6b $s12My_Toy_Chest22SharedDataActionFigureC03getfg26SpecificsICloudForCompletefG5ArrayyyFySaySo8CKRecordCGSg_s5Error_pSgtcfU_ + 3051
7 My Toy Chest 0x000000010931409c $s12My_Toy_Chest22SharedDataActionFigureC03getfg26SpecificsICloudForCompletefG5ArrayyyFySaySo8CKRecordCGSg_s5Error_pSgtcfU_TA + 12
8 My Toy Chest 0x0000000109315233 $s12My_Toy_Chest22SharedDataActionFigureC013fetchCompletefG18SpecificsFromCloud10completionyySaySo8CKRecordCGSg_s5Error_pSgtc_tFySo13CKQueryCursorCSg_AKtcfU0_ + 1331
9 My Toy Chest 0x0000000109315e01 $s12My_Toy_Chest22SharedDataActionFigureC013fetchCompletefG18SpecificsFromCloud10completionyySaySo8CKRecordCGSg_s5Error_pSgtc_tFySo13CKQueryCursorCSg_AKtcfU0_TA + 49
10 My Toy Chest 0x00000001092373b4 $sSo13CKQueryCursorCSgs5Error_pSgIeggg_ACSo7NSErrorCSgIeyByy_TR + 132
11 CloudKit 0x000000010f51e84d -[CKQueryOperation _finishOnCallbackQueueWithError:] + 613
12 CloudKit 0x000000010f51795a -[CKOperation _finishInternalOnCallbackQueueWithError:] + 582
13 CloudKit 0x000000010f5176fa -[CKOperation _handleCompletionCallback:] + 148
14 CloudKit 0x000000010f51e521 -[CKQueryOperation _handleCompletionCallback:] + 197
15 CloudKit 0x000000010f58d438 __82-[CKOperationCallbackManager _performCallbackForOperation:callback:errorCallback:]_block_invoke + 288
16 libdispatch.dylib 0x000000011189e725 _dispatch_block_async_invoke2 + 83
17 libdispatch.dylib 0x0000000111890db5 _dispatch_client_callout + 8
18 libdispatch.dylib 0x0000000111898225 _dispatch_lane_serial_drain + 778
19 libdispatch.dylib 0x0000000111898ed0 _dispatch_lane_invoke + 477
20 libdispatch.dylib 0x00000001118a2ea3 _dispatch_workloop_worker_thread + 733
21 libsystem_pthread.dylib 0x0000000111c79611 _pthread_wqthread + 421
22 libsystem_pthread.dylib 0x0000000111c793fd start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException
我很确定发生缺陷的代码是以下一组代码,它更新了 View Controller 集合视图中的 UI。
以下代码崩溃(异步)
DispatchQueue.main.async {
self.delegate?.updateActionFigureSpecificsModel()
}
以下代码不会崩溃(同步)
DispatchQueue.main.sync {
self.delegate?.updateActionFigureSpecificsModel()
}
更新视图控制器集合视图的后续调用
func updateActionFigureSpecificsModel() {
DispatchQueue.main.async {
self.delegate?.updateActionFigureModel(forActionFigure: self, forDelegateProperty: .specifics)
}
}
func updateActionFigureModel(forActionFigure actionFigure: ActionFigure, forDelegateProperty delegateProperty: DelegateProperty) {
switch delegateProperty {
case .specifics, .eBaySaleSummary:
DispatchQueue.main.async {
self.setUIActionFigure(forActionFigure: actionFigure)
}
}
}
func setUIActionFigure(forActionFigure actionFigure: ActionFigure) {
if let indexPath = getIndexPath(forActionFigure: actionFigure) {
DispatchQueue.main.async {
self.collectionView.reloadItems(at: [indexPath])
}
}
我真的无法从你发布的内容中判断出确切的问题是什么,但你可能 运行正在处理以下一个或两个一般问题:
- 基础 collection 不是 thread-safe 并且不能从多个线程修改。
- 基础 collection 在迭代时无法修改。
我怀疑 collection 你 using/iterating/modifying 在你正在 运行 的任何线程中与你正在完成的任务 use/iteration/modification 发生冲突正在 main
调度。是的,我怀疑将 async
更改为 sync
只是通过等待 main
任务在后台线程恢复之前完成来回避问题。 通常,如果后台线程和主线程共享对同一个基础的引用collection——并且它们中的任何一个都修改了那个collection——你最终会运行 遇到问题...除非你非常非常小心。
我经常使用的一个解决方案是将我的 collection 分开。我的后台线程将进行更新或执行更新 collection 的计算。然后我将复制 collection 并将该副本传递给 main
以供显示和 UI 交互。