在某些情况下,调用NSManagedObjectContext保存不会返回

时间:2013-10-17 18:47:08

标签: ios core-data nsmanagedobjectcontext

我对Core Data相对较新,正在寻找一些建议。 我的项目:我在这里有一些对象:   - SMCoreData,主要是围绕CoreData自动生成的代码    项目   - SMUploader,用于索引ALAssetLibrary中的资源,将结果写入SMCoreData,并具有上传照片的队列机制。   - SMUploaderViewController一个UICollectionView和一些显示队列进度的开关。

这些课程在很大程度上都很有用。当用户启动应用程序时,他们的相机胶卷将被编入索引并开始上传。它将成功上传1000张照片。

稍后将更多内容添加到上传队列时会出现问题。我正在观察对ALAssetLibrary的更改(比如有人使用Safari保存照片)。我的应用程序将收到通知,对图像进行排队,并在用户翻转开关时尝试上传(与之前相同)。

这是我遇到怪癖的地方。我首先从核心数据中获取下一个对象,从URL(核心数据对象)获取资产,然后将文件复制到我的沙箱中。然后我用filesize和temporaryFilePath更新CoreData。然而,对[context save]的调用永远不会返回(但它之前已经工作了很多次)。处理器为0%,它只是坐在那里没有返回。听起来像一个队列问题,但我使用的是后台队列:

MYCoreData主要是来自CoreData项目的样板代码。我把它移到了一个类来封装它。我确实改变了创建managedObjectContext的方式:

_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];

MYUploader有两个队列。一个用于索引资产库,另一个用于处理上载队列。

MYUploaderViewController使用MYUploader注册一些calllback块,以便可以呈现UI更新。这些调用总是在主队列中使用MYUploader完成。

一点代码:

-(void)uploadAsset:(SMUploadAsset*)uploadAsset completion:(SMUploaderBoolBlock)completion{
        // 1.) Dump asset to a temporary file to operate with
        // 1a) Update core data with temporaryFile, size, and md5
        // 2.) Create asset on our server
        // 2a) Update core data with s3URL
        // 3.) Upload file to s3RUL
        // 4.) Tell our server that the upload has completed
        // 5.) Update core data with uploaded and uploadedDate
        // 6.) Delete temporary file
        // 7.) Remove the uploadAsset from the queue

        NSLog(@"%s", __func__);
        if(uploadAsset.image){
            NSLog(@"........................................................... Uploading Image ................................................ ");
            uploadAsset.uploadQueueState = SMUploadAssetQueueStateInProgress;
            // 1.) Dump asset to a temporary file to operate with
            NSLog(@"Upload step 1. ");
            NSURL *assetURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@", uploadAsset.image.uri]];
            NSLog(@"--- UPLOAD 1.) Dumping file");
            [SMUploaderImageRipper bytesFromObject:assetURL completion:^(SMUploadProperties *uploadProperties) {
                if(uploadProperties == nil){
                    NSLog(@"Upload step 1 FAILED");
                    uploadAsset.uploadQueueState = SMUploadAssetQueueStateFailed;
                    NSLog(@"****** UPLOAD ERROR: Failed to dump asset to temporary file");
                    completion(NO);
                    return;
                }
                NSLog(@"Upload step 1 complete");

                // 1a) Update core data with temporaryFile, size, and md5
                NSLog(@"Upload step 1a. Updating core data with temp file, size, and md5");
                [self updateUploadAsset:uploadAsset uploadProperties:uploadProperties completion:^(BOOL success) {
                    if(success == NO){
                        NSLog(@"Upload step 1a FAILED");
                        uploadAsset.uploadQueueState = SMUploadAssetQueueStateFailed;
                        NSLog(@"****** UPLOAD ERROR: Failed to add temporaryFile, size, and md5 to CoreData");
                        completion(NO);
                        return;
                    }
                    NSLog(@"Upload step 1a complete");
    ....

然后更新CoreData对象的方法:

-(void)updateUploadAsset:(SMUploadAsset*)uploadAsset
        uploadProperties:(SMUploadProperties*)uploadProperties
              completion:(SMUploaderBoolBlock)completion{

        SMCoreData *coreData = [SMCoreData sharedInstance];
        NSManagedObjectContext *context = [coreData managedObjectContext];
        uploadAsset.md5 = uploadProperties.hash;
        uploadAsset.temporaryFile = uploadProperties.temporaryFile;
        uploadAsset.size = uploadProperties.size;
        NSError *cdError;
        // THIS CALL NEVER RETURNS
        if (![context save:&cdError]) {
            NSLog(@"Whoops, couldn't save: %@", [cdError localizedDescription]);
            completion(NO);
        }
        completion(YES);
}

我很乐意发布更多代码或回答问题,因为我很困难。

更新:在我看来,ALAssetLibarary观察员以某种方式导致了这个问题。这是我正在为通知做的事情。我已经尝试过使用主队列以及我拥有的不同后台队列,但这总是让CoreData进入时髦状态。如果我对此进行评论,那么按照上面的描述运行应用程序(转到safari,保存图像,返回)但是我没有收到此通知,而只是按下一个调用相同代码的按钮(enqueueCameraRollWithCompletion),everthing效果很好。

这里有什么解决方案?我真的想保留这个功能。

[[NSNotificationCenter defaultCenter] addObserverForName:ALAssetsLibraryChangedNotification                                                   对象:无                                                    队列:[NSOperationQueue mainQueue]                                               usingBlock:^(NSNotification * note){                                                   dispatch_async(self.processingQueue,^ {                                                       [self enqueueCameraRollWithCompletion:^(BOOL success){                                                       }];                                                   });                                               }];

编辑:使用performBlockAndWait

请求代码更新
-(void)updateUploadAsset:(SMUploadAsset*)uploadAsset
        uploadProperties:(SMUploadProperties*)uploadProperties
              completion:(SMUploaderBoolBlock)completion{

    SMCoreData *coreData = [SMCoreData sharedInstance];
    NSManagedObjectContext *context = [coreData managedObjectContext];

    [context performBlockAndWait:^{
        uploadAsset.md5 = uploadProperties.hash;
        uploadAsset.temporaryFile = uploadProperties.temporaryFile;
        uploadAsset.size = uploadProperties.size;

        if ([coreData saveContext]){
            completion(YES);
        }
        else{
            completion(NO);
        }

    }];
}

0 个答案:

没有答案