Core Data concurrency `performBlockAndWait:` NSManagedObjectContext zombie

后端 未结 2 1863
一向
一向 2021-01-26 11:58

I have a following crash report from my released app:

synchronizeMyWords method fetches the entities from database, creates private queue context w

相关标签:
2条回答
  • 2021-01-26 12:08

    I marked @Mundi answer as correct, because he wrote the general approach you should follow. Now, I want to share here how I debugged it. Firstly, I learned that, it is available to turn on debug concurrency assertion in xcode. You need to pass following argument on launch:

    -com.apple.CoreData.ConcurrencyDebug 1

    Now, in your application output, you should see log message:

    2016-12-12 01:58:31.665 your-app[4267:2180376] CoreData: annotation: Core Data multi-threading assertions enabled.

    Once I turned it on, my app crashed in synchronizeMyWords method (honestly, not only there. Wondering, why Apple does not include concurrency assertions by default in debug mode?). I checked what defaultExecutor is in AWSCore library and saw this:

    + (instancetype)defaultExecutor {
        static AWSExecutor *defaultExecutor = NULL;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            defaultExecutor = [self executorWithBlock:^void(void(^block)()) {
                // We prefer to run everything possible immediately, so that there is callstack information
                // when debugging. However, we don't want the stack to get too deep, so if the remaining stack space
                // is less than 10% of the total space, we dispatch to another GCD queue.
                size_t totalStackSize = 0;
                size_t remainingStackSize = remaining_stack_size(&totalStackSize);
    
                if (remainingStackSize < (totalStackSize / 10)) {
                    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block);
                } else {
                    @autoreleasepool {
                        block();
                    }
                }
            }];
        });
        return defaultExecutor;
    }
    

    According to their if statement, my continuationBlock was not guaranteed to be executed on DISPATCH_QUEUE_PRIORITY_DEFAULT queue. So, I created one shared dispatch_queue_t queue and call all operations on it combining with performBlockAndWait: CoreData method. As a result, there are no crashes now and I submitted new release. I will update this post, if I do not get any crash report with context zombie.

    0 讨论(0)
  • 2021-01-26 12:16

    Core Data provides ample APIs to deal with background threads. These are also accessible via Magical Record.

    It looks as if you creating too many threads unnecessarily. I think that the employment of AWSContinuationBlock and AWSExecutor is not a good idea. synchronizeMyWords could be called from a background thread. The block might be run on a background thread. Inside the block you create a new background thread linked to the child context. It is not clear what loadLocalWords returns, or how continueWithExecutor:block: deals with threads.

    There is also a problem with the saving of the data. The main context is not saved after the child context is saved; presumably this happens later, but perhaps in connection with some other operation, so that the fact that your code was working before is perhaps more of a "false positive".

    My recommendation is to simplify the threading code. You should confine yourself to the Core Data block APIs.

    0 讨论(0)
提交回复
热议问题