Ios CoreData保存在iPad Mini上的GCD后台队列挂断

Ios CoreData保存在iPad Mini上的GCD后台队列挂断,ios,ipad,core-data,grand-central-dispatch,ipad-mini,Ios,Ipad,Core Data,Grand Central Dispatch,Ipad Mini,我有一个单例类,它使用GCD(grandcentraldispatch)队列在后台将JSON对象保存到CoreData数据库。这在大多数情况下都能很好地工作,但在iPad2和iPadMini设备上,我遇到了冻结过程中的一些问题 我的设置非常简单。我有一个设置为串行运行的后台调度队列(backgroundQueue),我有一个单独的NSManagedObjectContext实例作为后台队列。当我想将某些内容保存到数据库时,我调用开始保存过程的方法,在该方法中,我使用dispatch_async在

我有一个单例类,它使用GCD(grandcentraldispatch)队列在后台将JSON对象保存到CoreData数据库。这在大多数情况下都能很好地工作,但在iPad2和iPadMini设备上,我遇到了冻结过程中的一些问题

我的设置非常简单。我有一个设置为串行运行的后台调度队列(backgroundQueue),我有一个单独的NSManagedObjectContext实例作为后台队列。当我想将某些内容保存到数据库时,我调用开始保存过程的方法,在该方法中,我使用dispatch_async在后台线程上调用我的保存逻辑

所有处理逻辑运行后,我保存后台MOC,并使用NSManagedObjectContextDidSaveNotification将后台MOC的更改合并到主线程MOC。后台线程等待此操作完成,一旦完成,它将运行队列中的下一个块(如果有)。下面是我的代码

dispatch_queue_t backgroundQueue;

- (id)init
{
    if (self = [super init]) 
    {
        // init the background queue
        backgroundQueue = dispatch_queue_create(VS_CORE_DATA_MANAGER_BACKGROUND_QUEUE_NAME, DISPATCH_QUEUE_SERIAL);
    }
    return self;
}

- (void)saveJsonObjects:(NSDictionary *)jsonDict
          objectMapping:(VS_ObjectMapping *)objectMapping
                  class:(__unsafe_unretained Class)managedObjectClass
             completion:(void (^)(NSArray *objects, NSError *error))completion
{
    [VS_Log logDebug:@"**** Queue Save JSON Objects for Class: %@", managedObjectClass];

    // process in the background queue
    dispatch_async(backgroundQueue, ^(void)
    {
        [VS_Log logDebug:@"**** Dispatch Save JSON Objects for Class: %@", managedObjectClass];

        // create a new process object and add it to the dictionary
        currentRequest = [[VS_CoreDataRequest alloc] init];

        // set the thead name
        NSThread *currentThread = [NSThread currentThread];
        [currentThread setName:VS_CORE_DATA_MANAGER_BACKGROUND_THREAD_NAME];

        // if there is not already a background context, then create one
        if (!_backgroundQueueManagedObjectContext)
        {
            NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
            if (coordinator != nil)
            {
                // create the background queue
                _backgroundQueueManagedObjectContext = [[NSManagedObjectContext alloc] init];
                [_backgroundQueueManagedObjectContext setPersistentStoreCoordinator:coordinator];
                [_backgroundQueueManagedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
                _backgroundQueueManagedObjectContext.undoManager = nil;

                // listen for the merge changes from context did save notification on the background queue
                [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChangesFromBackground:) name:NSManagedObjectContextDidSaveNotification object:_backgroundQueueManagedObjectContext];
            }
        }

        // save the JSON dictionary
        NSArray *objects = [self saveJSON:jsonDict objectMapping:objectMapping class:managedObjectClass managedObjectContext:_backgroundQueueManagedObjectContext level:0];

        // save the objects so we can access them later to be re-fetched and returned on the main thread
        if (objects.count > 0)
            [currentRequest.objects addObjectsFromArray:objects];

        // save the object IDs and the completion block to global variables so we can access them after the save
        if (completion)
            currentRequest.completionBlock = completion;

        [VS_Log logDebug:@"**** Save MOC for Class: %@", managedObjectClass];

        // save all changes object context
        [self saveManagedObjectContext];
    });
}

- (void)mergeChangesFromBackground:(NSNotification *)notification
{
    [VS_Log logDebug:@"**** Merge Changes From Background"];

    // save the current request to a local var since we're about to be processing it on the main thread
    __block VS_CoreDataRequest *request = (VS_CoreDataRequest *)[currentRequest copy];
    __block NSNotification *bNotification = (NSNotification *)[notification copy];

    // clear out the request
    currentRequest = nil;

    dispatch_sync(dispatch_get_main_queue(), ^(void)
    {
        [VS_Log logDebug:@"**** Start Merge Changes On Main Thread"];

        // merge changes to the primary context, and wait for the action to complete on the main thread
        [_managedObjectContext mergeChangesFromContextDidSaveNotification:bNotification];

        NSMutableArray *objects = [[NSMutableArray alloc] init];

        // iterate through the updated objects and find them in the main thread MOC
        for (NSManagedObject *object in request.objects)
        {
            NSError *error;
            NSManagedObject *obj = [[self managedObjectContext] existingObjectWithID:object.objectID error:&error];
            if (error)
                [self logError:error];

            if (obj)
                [objects addObject:obj];
        }

        // call the completion block
        if (request.completionBlock)
        {
            void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
            saveCompletionBlock(objects, nil);
        }

        [VS_Log logDebug:@"**** Complete Merge Changes On Main Thread"];

        // clear the request
        request = nil;
    });

    [VS_Log logDebug:@"**** Complete Merge Changes From Background"];
}
当问题发生时,一切似乎都正常运行,并进入mergeChangesFromBackground:方法,但不会调用dispatch\u async()

正如我上面提到的,代码在大多数情况下都能完美运行。只有在我在iPad2或iPadMini上测试并保存大型对象时,它才会出现运行问题

有人知道为什么会这样吗

谢谢

**编辑**

自撰写本文以来,我了解了嵌套的NSManagedObjectContext。我已经修改了我的代码来使用它,它似乎工作得很好。然而,我仍然有同样的问题。思想?另外,对嵌套MOC有何评论

- (void)saveJsonObjects:(NSDictionary *)jsonDict
          objectMapping:(VS_ObjectMapping *)objectMapping
                  class:(__unsafe_unretained Class)managedObjectClass
             completion:(void (^)(NSArray *objects, NSError *error))completion
{
    // process in the background queue
    dispatch_async(backgroundQueue, ^(void)
    {
        // create a new process object and add it to the dictionary
        __block VS_CoreDataRequest *currentRequest = [[VS_CoreDataRequest alloc] init];

        // set the thead name
        NSThread *currentThread = [NSThread currentThread];
        [currentThread setName:VS_CORE_DATA_MANAGER_BACKGROUND_THREAD_NAME];

        // if there is not already a background context, then create one
        if (!_backgroundQueueManagedObjectContext)
        {
            NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
            if (coordinator != nil)
            {
                // create the background queue
                _backgroundQueueManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
                [_backgroundQueueManagedObjectContext setParentContext:[self managedObjectContext]];
                [_backgroundQueueManagedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
                _backgroundQueueManagedObjectContext.undoManager = nil;
            }
        }

        // save the JSON dictionary starting at the upper most level of the key path
        NSArray *objects = [self saveJSON:jsonDict objectMapping:objectMapping class:managedObjectClass managedObjectContext:_backgroundQueueManagedObjectContext level:0];

        // if no objects were processed, then return with an error
        if (!objects || objects.count == 0)
        {
            currentRequest = nil;

            // dispatch the completion block
            dispatch_async(dispatch_get_main_queue(), ^(void)
            {
                NSError *error = [self createErrorWithErrorCode:100 description:@"No objects were found for the object mapping"];
                [self logMessage:error.debugDescription];
                completion(nil, error);
            });
        }

        // save the objects so we can access them later to be re-fetched and returned on the main thread
        if (objects.count > 0)
            [currentRequest.objects addObjectsFromArray:objects];

        // save the object IDs and the completion block to global variables so we can access them after the save
        if (completion)
            currentRequest.completionBlock = completion;

        // save all changes object context
        NSError *error = nil;
        [_backgroundQueueManagedObjectContext save:&error];
        if (error)
            [VS_Log logError:error.localizedDescription];

        dispatch_sync(dispatch_get_main_queue(), ^(void)
        {
            NSMutableArray *objects = [[NSMutableArray alloc] init];

            // iterate through the updated objects and find them in the main thread MOC
            for (NSManagedObject *object in currentRequest.objects)
            {
                NSError *error;
                NSManagedObject *obj = [[self managedObjectContext] existingObjectWithID:object.objectID error:&error];
                if (error)
                    [self logError:error];

                if (obj)
                    [objects addObject:obj];
            }

            // call the completion block
            if (currentRequest.completionBlock)
            {
                void (^saveCompletionBlock)(NSArray *, NSError *) = currentRequest.completionBlock;
                saveCompletionBlock(objects, nil);
            }
        });

        // clear out the request
        currentRequest = nil;
    });
}
**编辑**

大家好,


首先,我要感谢大家在这方面的投入,因为这正是我找到问题解决方案的原因。长话短说,我完全放弃了使用GCD和自定义后台队列,而是使用嵌套上下文和“performBlock”方法在后台线程上执行所有保存。这很有效,我提出了我的挂断问题

然而,我现在有一个新的bug。每当我第一次运行我的应用程序,每当我试图保存具有子对象关系的对象时,我都会遇到以下异常

-_仅为抽象类定义了referenceData64。定义-[NSTemporaryObjectID\u默认值\u referenceData64]

下面是我的新代码

- (void)saveJsonObjects:(NSDictionary *)jsonDict
          objectMapping:(VS_ObjectMapping *)objectMapping
                  class:(__unsafe_unretained Class)managedObjectClass
             completion:(void (^)(NSArray *objects, NSError *error))completion
{
    [_backgroundManagedObjectContext performBlock:^(void)
    {
        // create a new process object and add it to the dictionary
        VS_CoreDataRequest *currentRequest = [[VS_CoreDataRequest alloc] init];
        currentRequest.managedObjectClass = managedObjectClass;

        // save the JSON dictionary starting at the upper most level of the key path
        NSArray *objects = [self saveJSON:jsonDict objectMapping:objectMapping class:managedObjectClass managedObjectContext:_backgroundManagedObjectContext level:0];

        if (objects.count == 0)
        {
            currentRequest.error = [self createErrorWithErrorCode:100 description:@"No objects were found for the object mapping"];
            [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidFail:) withObject:currentRequest waitUntilDone:NO];
        }
        else
        {
            // save the objects so we can access them later to be re-fetched and returned on the main thread
            [currentRequest.objects addObjectsFromArray:objects];

            // save the object IDs and the completion block to global variables so we can access them after the save
            currentRequest.completionBlock = completion;

            [_backgroundManagedObjectContext lock];

            @try
            {
                [_backgroundManagedObjectContext processPendingChanges];
                [_backgroundManagedObjectContext save:nil];
            }
            @catch (NSException *exception)
            {
                currentRequest.error = [self createErrorWithErrorCode:100 description:exception.reason];
            }

            [_backgroundManagedObjectContext unlock];

            // complete the process on the main thread
            if (currentRequest.error)
                [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidFail:) withObject:currentRequest waitUntilDone:NO];
            else
                [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidSucceed:) withObject:currentRequest waitUntilDone:NO];

            // clear out the request
            currentRequest = nil;
        }
    }];
}

- (void)backgroundSaveProcessDidFail:(VS_CoreDataRequest *)request
{
    if (request.error)
        [self logError:request.error];

    if (request.completionBlock)
    {
        void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
        saveCompletionBlock(nil, request.error);
    }
}

- (void)backgroundSaveProcessDidSucceed:(VS_CoreDataRequest *)request
{
    // get objects from main thread
    NSArray *objects = nil;
    if (request.objects.count > 0)
    {
        NSFetchRequest *fetchReq = [NSFetchRequest fetchRequestWithEntityName:[NSString stringWithFormat:@"%@", request.managedObjectClass]];
        fetchReq.predicate = [NSPredicate predicateWithFormat:@"self IN %@", request.objects];
        objects = [self executeFetchRequest:fetchReq];
    }

    // call the completion block
    if (request.completionBlock)
    {
        void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
        saveCompletionBlock(objects, nil);
    }
}

- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil)
    {
        // create the MOC for the backgroumd thread
        _backgroundManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_backgroundManagedObjectContext setPersistentStoreCoordinator:coordinator];
        [_backgroundManagedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
        _backgroundManagedObjectContext.undoManager = nil;

        // create the MOC for the main thread
        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_managedObjectContext setParentContext:_backgroundManagedObjectContext];
        [_managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
    }

    return _managedObjectContext;
}
- (void)saveJsonObjects:(NSDictionary *)jsonDict
          objectMapping:(VS_ObjectMapping *)objectMapping
                  class:(__unsafe_unretained Class)managedObjectClass
             completion:(void (^)(NSArray *objects, NSError *error))completion
{
    [_backgroundManagedObjectContext performBlock:^(void)
    {
        // create a new process object and add it to the dictionary
        VS_CoreDataRequest *currentRequest = [[VS_CoreDataRequest alloc] init];
        currentRequest.managedObjectClass = managedObjectClass;

        // save the JSON dictionary starting at the upper most level of the key path
        NSArray *objects = [self saveJSON:jsonDict objectMapping:objectMapping class:managedObjectClass managedObjectContext:_backgroundManagedObjectContext level:0];

        if (objects.count == 0)
        {
            currentRequest.error = [self createErrorWithErrorCode:100 description:@"No objects were found for the object mapping"];
            [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidFail:) withObject:currentRequest waitUntilDone:NO];
        }
        else
        {
            // save the objects so we can access them later to be re-fetched and returned on the main thread
            [currentRequest.objects addObjectsFromArray:objects];

            // save the object IDs and the completion block to global variables so we can access them after the save
            currentRequest.completionBlock = completion;

            [_backgroundManagedObjectContext lock];

            @try
            {
                [_backgroundManagedObjectContext processPendingChanges];
                [_backgroundManagedObjectContext save:nil];
            }
            @catch (NSException *exception)
            {
                currentRequest.error = [self createErrorWithErrorCode:100 description:exception.reason];
            }

            [_backgroundManagedObjectContext unlock];

            // complete the process on the main thread
            if (currentRequest.error)
                [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidFail:) withObject:currentRequest waitUntilDone:NO];
            else
                [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidSucceed:) withObject:currentRequest waitUntilDone:NO];

            // clear out the request
            currentRequest = nil;
        }
    }];
}

- (void)backgroundSaveProcessDidFail:(VS_CoreDataRequest *)request
{
    if (request.error)
        [self logError:request.error];

    if (request.completionBlock)
    {
        void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
        saveCompletionBlock(nil, request.error);
    }
}

- (void)backgroundSaveProcessDidSucceed:(VS_CoreDataRequest *)request
{
    // get objects from main thread
    NSArray *objects = nil;
    if (request.objects.count > 0)
    {
        NSFetchRequest *fetchReq = [NSFetchRequest fetchRequestWithEntityName:[NSString stringWithFormat:@"%@", request.managedObjectClass]];
        fetchReq.predicate = [NSPredicate predicateWithFormat:@"self IN %@", request.objects];
        objects = [self executeFetchRequest:fetchReq];
    }

    // call the completion block
    if (request.completionBlock)
    {
        void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
        saveCompletionBlock(objects, nil);
    }
}

- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil)
    {
        // create the MOC for the backgroumd thread
        _backgroundManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_backgroundManagedObjectContext setPersistentStoreCoordinator:coordinator];
        [_backgroundManagedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
        _backgroundManagedObjectContext.undoManager = nil;

        // create the MOC for the main thread
        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_managedObjectContext setParentContext:_backgroundManagedObjectContext];
        [_managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
    }

    return _managedObjectContext;
}

你有没有想过为什么会发生这样的崩溃?

< P>你可能想考虑这种上下文架构(未经测试的代码):
mainManagedObjectContext将以相同的方式初始化,但使用
NSMainQueueConcurrencyType
且无观察者(顺便说一句,请记住删除观察者)


首先,我要感谢大家在这方面的投入,因为这正是我找到问题解决方案的原因。长话短说,我完全放弃了使用GCD和自定义后台队列,而是使用嵌套上下文和“performBlock”方法在后台线程上执行所有保存。这很有效,我提出了我的挂断问题

然而,我现在有一个新的bug。每当我第一次运行我的应用程序,每当我试图保存具有子对象关系的对象时,我都会遇到以下异常

-_仅为抽象类定义了referenceData64。定义-[NSTemporaryObjectID\u默认值\u referenceData64]

下面是我的新代码

- (void)saveJsonObjects:(NSDictionary *)jsonDict
          objectMapping:(VS_ObjectMapping *)objectMapping
                  class:(__unsafe_unretained Class)managedObjectClass
             completion:(void (^)(NSArray *objects, NSError *error))completion
{
    [_backgroundManagedObjectContext performBlock:^(void)
    {
        // create a new process object and add it to the dictionary
        VS_CoreDataRequest *currentRequest = [[VS_CoreDataRequest alloc] init];
        currentRequest.managedObjectClass = managedObjectClass;

        // save the JSON dictionary starting at the upper most level of the key path
        NSArray *objects = [self saveJSON:jsonDict objectMapping:objectMapping class:managedObjectClass managedObjectContext:_backgroundManagedObjectContext level:0];

        if (objects.count == 0)
        {
            currentRequest.error = [self createErrorWithErrorCode:100 description:@"No objects were found for the object mapping"];
            [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidFail:) withObject:currentRequest waitUntilDone:NO];
        }
        else
        {
            // save the objects so we can access them later to be re-fetched and returned on the main thread
            [currentRequest.objects addObjectsFromArray:objects];

            // save the object IDs and the completion block to global variables so we can access them after the save
            currentRequest.completionBlock = completion;

            [_backgroundManagedObjectContext lock];

            @try
            {
                [_backgroundManagedObjectContext processPendingChanges];
                [_backgroundManagedObjectContext save:nil];
            }
            @catch (NSException *exception)
            {
                currentRequest.error = [self createErrorWithErrorCode:100 description:exception.reason];
            }

            [_backgroundManagedObjectContext unlock];

            // complete the process on the main thread
            if (currentRequest.error)
                [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidFail:) withObject:currentRequest waitUntilDone:NO];
            else
                [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidSucceed:) withObject:currentRequest waitUntilDone:NO];

            // clear out the request
            currentRequest = nil;
        }
    }];
}

- (void)backgroundSaveProcessDidFail:(VS_CoreDataRequest *)request
{
    if (request.error)
        [self logError:request.error];

    if (request.completionBlock)
    {
        void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
        saveCompletionBlock(nil, request.error);
    }
}

- (void)backgroundSaveProcessDidSucceed:(VS_CoreDataRequest *)request
{
    // get objects from main thread
    NSArray *objects = nil;
    if (request.objects.count > 0)
    {
        NSFetchRequest *fetchReq = [NSFetchRequest fetchRequestWithEntityName:[NSString stringWithFormat:@"%@", request.managedObjectClass]];
        fetchReq.predicate = [NSPredicate predicateWithFormat:@"self IN %@", request.objects];
        objects = [self executeFetchRequest:fetchReq];
    }

    // call the completion block
    if (request.completionBlock)
    {
        void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
        saveCompletionBlock(objects, nil);
    }
}

- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil)
    {
        // create the MOC for the backgroumd thread
        _backgroundManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_backgroundManagedObjectContext setPersistentStoreCoordinator:coordinator];
        [_backgroundManagedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
        _backgroundManagedObjectContext.undoManager = nil;

        // create the MOC for the main thread
        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_managedObjectContext setParentContext:_backgroundManagedObjectContext];
        [_managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
    }

    return _managedObjectContext;
}
- (void)saveJsonObjects:(NSDictionary *)jsonDict
          objectMapping:(VS_ObjectMapping *)objectMapping
                  class:(__unsafe_unretained Class)managedObjectClass
             completion:(void (^)(NSArray *objects, NSError *error))completion
{
    [_backgroundManagedObjectContext performBlock:^(void)
    {
        // create a new process object and add it to the dictionary
        VS_CoreDataRequest *currentRequest = [[VS_CoreDataRequest alloc] init];
        currentRequest.managedObjectClass = managedObjectClass;

        // save the JSON dictionary starting at the upper most level of the key path
        NSArray *objects = [self saveJSON:jsonDict objectMapping:objectMapping class:managedObjectClass managedObjectContext:_backgroundManagedObjectContext level:0];

        if (objects.count == 0)
        {
            currentRequest.error = [self createErrorWithErrorCode:100 description:@"No objects were found for the object mapping"];
            [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidFail:) withObject:currentRequest waitUntilDone:NO];
        }
        else
        {
            // save the objects so we can access them later to be re-fetched and returned on the main thread
            [currentRequest.objects addObjectsFromArray:objects];

            // save the object IDs and the completion block to global variables so we can access them after the save
            currentRequest.completionBlock = completion;

            [_backgroundManagedObjectContext lock];

            @try
            {
                [_backgroundManagedObjectContext processPendingChanges];
                [_backgroundManagedObjectContext save:nil];
            }
            @catch (NSException *exception)
            {
                currentRequest.error = [self createErrorWithErrorCode:100 description:exception.reason];
            }

            [_backgroundManagedObjectContext unlock];

            // complete the process on the main thread
            if (currentRequest.error)
                [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidFail:) withObject:currentRequest waitUntilDone:NO];
            else
                [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidSucceed:) withObject:currentRequest waitUntilDone:NO];

            // clear out the request
            currentRequest = nil;
        }
    }];
}

- (void)backgroundSaveProcessDidFail:(VS_CoreDataRequest *)request
{
    if (request.error)
        [self logError:request.error];

    if (request.completionBlock)
    {
        void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
        saveCompletionBlock(nil, request.error);
    }
}

- (void)backgroundSaveProcessDidSucceed:(VS_CoreDataRequest *)request
{
    // get objects from main thread
    NSArray *objects = nil;
    if (request.objects.count > 0)
    {
        NSFetchRequest *fetchReq = [NSFetchRequest fetchRequestWithEntityName:[NSString stringWithFormat:@"%@", request.managedObjectClass]];
        fetchReq.predicate = [NSPredicate predicateWithFormat:@"self IN %@", request.objects];
        objects = [self executeFetchRequest:fetchReq];
    }

    // call the completion block
    if (request.completionBlock)
    {
        void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
        saveCompletionBlock(objects, nil);
    }
}

- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil) {
        return _managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil)
    {
        // create the MOC for the backgroumd thread
        _backgroundManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_backgroundManagedObjectContext setPersistentStoreCoordinator:coordinator];
        [_backgroundManagedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
        _backgroundManagedObjectContext.undoManager = nil;

        // create the MOC for the main thread
        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_managedObjectContext setParentContext:_backgroundManagedObjectContext];
        [_managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
    }

    return _managedObjectContext;
}
你们有谁知道为什么会发生这场车祸吗

**编辑**

女士们先生们,我找到了解决办法。显然,即使使用嵌套上下文,也必须通过NSManagedObjectContextDidSaveNotification合并更改。一旦我把它添加到我的代码中,它就开始完美地工作了。下面是我的工作代码。万分感谢

- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil)
        return _managedObjectContext;

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil)
    {
        // create the MOC for the backgroumd thread
        _backgroundManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_backgroundManagedObjectContext setPersistentStoreCoordinator:coordinator];
        [_backgroundManagedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
        _backgroundManagedObjectContext.undoManager = nil;

        // create the MOC for the main thread
        _managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [_managedObjectContext setParentContext:_backgroundManagedObjectContext];
        [_managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChangesFromContextDidSaveNotification:) name:NSManagedObjectContextDidSaveNotification object:_backgroundManagedObjectContext];
    }

    return _managedObjectContext;
}

- (void)saveJsonObjects:(NSDictionary *)jsonDict
          objectMapping:(VS_ObjectMapping *)objectMapping
                  class:(__unsafe_unretained Class)managedObjectClass
             completion:(void (^)(NSArray *objects, NSError *error))completion
{
    // perform save on background thread
    [_backgroundManagedObjectContext performBlock:^(void)
    {
        // create a new process object and add it to the dictionary
        VS_CoreDataRequest *currentRequest = [[VS_CoreDataRequest alloc] init];
        currentRequest.managedObjectClass = managedObjectClass;

        // save the JSON dictionary starting at the upper most level of the key path
        NSArray *objects = [self saveJSON:jsonDict objectMapping:objectMapping class:managedObjectClass managedObjectContext:_backgroundManagedObjectContext level:0];

        if (objects.count == 0)
        {
            currentRequest.error = [self createErrorWithErrorCode:100 description:@"No objects were found for the object mapping"];
            [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidFail:) withObject:currentRequest waitUntilDone:NO];
        }
        else
        {
            // save the objects so we can access them later to be re-fetched and returned on the main thread
            [currentRequest.objects addObjectsFromArray:objects];

            // save the object IDs and the completion block to global variables so we can access them after the save
            currentRequest.completionBlock = completion;

            [_backgroundManagedObjectContext lock];

            @try
            {
                [_backgroundManagedObjectContext processPendingChanges];
                [_backgroundManagedObjectContext save:nil];
            }
            @catch (NSException *exception)
            {
                currentRequest.error = [self createErrorWithErrorCode:100 description:exception.reason];
            }

            [_backgroundManagedObjectContext unlock];

            // complete the process on the main thread
            if (currentRequest.error)
                [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidFail:) withObject:currentRequest waitUntilDone:NO];
            else
                [self performSelectorOnMainThread:@selector(backgroundSaveProcessDidSucceed:) withObject:currentRequest waitUntilDone:NO];

            // clear out the request
            currentRequest = nil;
        }
    }];
}

- (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification
{
    [_managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
}

- (void)backgroundSaveProcessDidFail:(VS_CoreDataRequest *)request
{
    if (request.error)
        [self logError:request.error];

    if (request.completionBlock)
    {
        void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
        saveCompletionBlock(nil, request.error);
    }
}

- (void)backgroundSaveProcessDidSucceed:(VS_CoreDataRequest *)request
{
    // get objects from main thread
    NSArray *objects = nil;
    if (request.objects.count > 0)
    {
        NSFetchRequest *fetchReq = [NSFetchRequest fetchRequestWithEntityName:[NSString stringWithFormat:@"%@", request.managedObjectClass]];
        fetchReq.predicate = [NSPredicate predicateWithFormat:@"self IN %@", request.objects];
        objects = [self executeFetchRequest:fetchReq];
    }

    // call the completion block
    if (request.completionBlock)
    {
        void (^saveCompletionBlock)(NSArray *, NSError *) = request.completionBlock;
        saveCompletionBlock(objects, nil);
    }
}

编辑后,您将进行额外的保存(在mainManagedObjectContext上),以便将更改保留到存储中。这意味着在主线程上执行IO操作,您可能希望避免这种情况。嵌套上下文非常棒。有一件事立刻引起了我的注意。您应该在主线程中创建一个私有队列类型的上下文,因为不允许您在主线程之外的任何其他线程中接触主上下文。换句话说,将[self-managedObjectContext]设置为后台线程上的父上下文是无效的。此外,您只能在作为[privateContext performBlock:]方法参数提供的块中触摸私有上下文。Svena,感谢您的评论。你的方法正是我决定采用的,而且似乎效果很好。然而,我现在有一个新问题。请参阅下面的答案。请注意,您正在合并后台线程中的更改。您的更改应该使用
performBlock/*和wait*/:
合并,这样您的主上下文就不会在后台线程中使用。