私は Core Data に比較的慣れていないので、アドバイスを求めています。私のプロジェクト: ここにいくつかのオブジェクトがあります: - 主に CoreData プロジェクトの自動生成コードである SMCoreData - ALAssetLibrary のアセットをインデックス化し、結果を SMCoreData に書き込み、写真をアップロードするためのキュー メカニズムを持つ SMUploader。- SMUploaderViewController UICollectionView と、キューの進行状況を示すいくつかのスイッチ。
これらのクラスは、ほとんどの場合うまく機能します。ユーザーがアプリを起動すると、カメラ ロールのインデックスが作成され、アップロードが開始されます。1000 枚の写真を正常にアップロードします。
問題は、後でアップロード キューに追加されたときに発生します。ALAssetLibrary への変更を観察しています (誰かが Safari を使用して写真を保存するとします)。私のアプリは通知を受け取り、画像をキューに入れ、ユーザーがスイッチを切り替えたときにアップロードを試みます (前と同じ)。
これは私が癖に遭遇しているところです。最初にコア データから次のオブジェクトを取得し、URL (コア データ オブジェクト) からアセットを取得してから、ファイルをサンドボックスにコピーします。次に、CoreData をファイルサイズと temporaryFilePath で更新します。ただし、[context save] への呼び出しは返されません (ただし、これまでに何千回も機能していました)。プロセッサは 0% で、戻ってこないだけです。最初はキューの問題のように聞こえますが、バックグラウンド キューを使用しています。
MYCoreData は、主に CoreData プロジェクトのボイラープレート コードです。カプセル化するクラスに移動しました。ただし、managedObjectContext の作成方法を変更しました。
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
MYUploader には 2 つのキューがあります。1 つはアセット ライブラリのインデックス作成用、もう 1 つはアップロード キューの処理用です。
MYUploaderViewController には、UI の更新をレンダリングできるように、いくつかのコールバック ブロックが MYUploader に登録されています。これらの呼び出しは常に 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 をファンキーな状態にするものです。これをコメントアウトしてから、上記のようにアプリを実行します (サファリに移動し、画像を保存して戻る) が、この通知を受け取る代わりに、この同じコード (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);
}
}];
}