我对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);
}
}];
}