0

私は 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);
        }

    }];
}
4

0 に答える 0