2

我有一个单例类,它使用 GCD(Grand Central Dispatch)队列在后台将 JSON 对象保存到 CoreData 数据库。这在大多数情况下都能完美运行,但在 iPad 2 和 iPad Mini 设备上,我遇到了一些进程冻结问题。

我的设置非常简单。我有一个设置为串行运行的后台调度队列 (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_async()。

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

有没有人知道为什么会这样?

谢谢

** 编辑 **

自从写这篇文章以来,我已经了解了嵌套的 NSManagedObjectContexts。我已经修改了我的代码以使用它,它似乎运行良好。但是,我仍然有同样的问题。想法?另外,对嵌套 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”方法在后台线程上执行所有保存。这很好用,我提出了挂断问题。

但是,我现在有一个新错误。每当我第一次运行我的应用程序时,每当我尝试保存具有子对象关系的对象时,都会出现以下异常。

-_referenceData64 仅为抽象类定义。定义 -[NSTemporaryObjectID_default _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;
}

你们中有人知道为什么会发生这种崩溃吗?

4

2 回答 2

2

您可能需要考虑这种上下文架构(未经测试的代码):
mainManagedObjectContext 将以相同的方式初始化,但NSMainQueueConcurrencyType没有观察者(顺便说一句,请记住删除您的观察者)

- (void) mergeChangesFromBackground:(NSNotification*)notification
{
    __block __weak NSManagedObjectContext* context = self.managedObjectContext;
    [context performBlockAndWait:^{
        [context mergeChangesFromContextDidSaveNotification:notification];
    }];
}

- (NSManagedObjectContext*) bgContext
{
    if (_bgContext) {
        return _bgContext;
    }

    NSPersistentStoreCoordinator* coordinator = [self persistentStoreCoordinator];
    if (!coordinator) {
        return nil;
    }
    _bgContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    [_bgContext setPersistentStoreCoordinator:coordinator];
    [_bgContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
    [_bgContext setUndoManager:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(mergeChangesFromBackground:)
                                                 name:NSManagedObjectContextDidSaveNotification
                                               object:_bgContext];
    return _bgContext;
}

- (void) doSomethingOnBGContextWithoutBlocking:(void(^)(NSManagedObjectContext*))contextBlock
{
    __block __weak NSManagedObjectContext* context = [self bgContext];
    [context performBlock:^{
        NSThread *currentThread = [NSThread currentThread];
        NSString* prevName = [currentThread name];
        [currentThread setName:@"BGContextThread"];

        contextBlock(context);

        [context reset];
        [currentThread setName:prevName];
    }];
}
于 2013-04-04T21:27:39.737 回答
0

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

但是,我现在有一个新错误。每当我第一次运行我的应用程序时,每当我尝试保存具有子对象关系的对象时,都会出现以下异常。

-_referenceData64 仅为抽象类定义。定义 -[NSTemporaryObjectID_default _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;
}

你们中有人知道为什么会发生这种崩溃吗?

** 编辑 **

女士们,先生们,我找到了解决方案。显然,即使使用嵌套上下文,您仍然必须通过 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);
    }
}
于 2013-04-05T14:52:34.533 回答