Jacksonsox
Jacksonsox

Reputation: 1233

Sync solves Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: > was mutated while being enumerated

I believe I've found a solution to the error, but don't understand if I'm just avoiding a race condition with my solution by slowing the code down or if I'm really solving the issue.

My question is 2 part
1) Why does changing dispatch.main.async to dispatch.main.sync make the error go away because I've coded it correctly or have I just worked around the issue for now?

2) Do I need to change all the dispatch.main.async to dispatch.main.sync for the 2 additional calls?

Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x600002a24180> was mutated while being enumerated.

In this report, the second answer specifically says, don't ever use dispatch.main.sync as it causes deadlocks. The first answer says, you may want to use sync but it can cause deadlocks.

What does main.sync in global().async mean?

Apple reports the error as follows:

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

When I run the code in Xcode, I can reproduce the error in Xcode and get the following stack trace.

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

I am pretty sure the code where the defect is occurring is the following set of code that updates the UI in a View Controller collection view.

The following code crashes (async)

    DispatchQueue.main.async {
        self.delegate?.updateActionFigureSpecificsModel()
    }

The following code doesn't crash (sync)

    DispatchQueue.main.sync {
        self.delegate?.updateActionFigureSpecificsModel()
    }

Subsequent calls that update the View Controller collection views

    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])
            }
        }

Upvotes: 0

Views: 976

Answers (1)

James Bucanek
James Bucanek

Reputation: 3439

I really can't tell from what you've posted what the exact problem is, but you're probably running into one or both of these general issues:

  • Foundation collections are not thread-safe and cannot be modified from multiple threads.
  • A foundation collection cannot be modified while it is being iterated.

I suspect that the collection you're using/iterating/modifying in whatever thread you're running is colliding with whatever use/iteration/modification that's being done by the tasks you're dispatching on main. And yes, I suspect that changing async to sync is just dodging the issue by waiting for the main task to complete before the background thread resumes. As a rule, if the background thread and main thread are sharing a reference to the same foundation collection–and either of them modify that collection–you're going to eventually run into problems ... unless you're very, very, careful.

A solution I often use is to keep my collections separate. My background thread will make updates or perform calculations that update a collection. Then I'll make a copy of the collection and pass that copy to main for display and UI interactions.

Upvotes: 2

Related Questions