0

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

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

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

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

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

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

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

MYUploaderViewController 向 MYUploader 注册了一些回调块,以便可以呈现 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),一切都很好。

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

[[NSNotificationCenter defaultCenter] addObserverForName:ALAssetsLibraryChangedNotification object:nil queue:[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);
        }

    }];
}
4

0 回答 0