4

デッドロックまたは同期の問題と思われるものを次のように「解決」しました。

    [NSThread sleepForTimeInterval:0.1];

IPODライブラリからのMPMediaItem(音楽/画像)プロパティ参照をオブジェクトインスタンスにアタッチするアプリで、これらのオブジェクトはCoreDataを介してバックストアされます。ここでの私の関心は、何が起こっているのか、そしてこの状況でのベストプラクティスは何かを正確に理解することです。ここに行きます:

これを毎回複製するレシピは次のとおりです。

  1. ユーザーが新しいプロジェクトを作成します。

    doc = [[UIManagedDocument alloc] initWithFileURL:docURL];
    
    if (![[NSFileManager defaultManager] fileExistsAtPath:[docURL path]]) {
        [doc saveToURL:docURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
        if (success) {
            completionBlock(doc);
        }
        else {
            DLog(@"Failed document creation: %@", doc.localizedName);
        }
    }];
    
  2. その後、managedObjectContextを使用して、オブジェクトインスタンスを関連付け、CoreDataモデルをハイドレイトします。

    TheProject *theProject = [TheProject projectWithInfo:theProjectInfo
                                  inManagedObjectContext:doc.managedObjectContext];
    
  3. ユーザーは後で「CustomAction」オブジェクトを作成し、それに「ChElement」を追加して、「MusicElement」をChElementに関連付けます。(これらはCoreDataモデルオブジェクトの仮名です)。MusicElementは、IPODライブラリを介して追加されます。

    #define PLAYER [MPMusicPlayerController iPodMusicPlayer]
    
  4. ユーザーはこのプロジェクトを保存してから、ChElementとMusicElementを使用して1つのCustomActionオブジェクトがすでに作成されている既存のプロジェクトに切り替えます。

  5. ユーザーは、tableViewからそのChElementを選択し、detailViewに移動します。ChElementTVC(Appleドキュメントにあるものと同様のCoreData TableViewControllerクラスのサブクラス)から移動する場合は、次のことが必要です。

    - (void)viewWillDisappear:(BOOL)animated
    {
        [super viewWillDisappear:animated];
        self.fetchedResultsController.delegate = nil;
    }
    
  6. 詳細ビューで、ユーザーはChElementオブジェクトの属性を変更し、プロジェクトを保存します。detailViewは、そのデリゲート(ChElementTVC)を呼び出して保存を行います。保存は、NSManagedObjectを保持するUIManagedDocumentインスタンスに行われます。

    #define SAVEDOC(__DOC__) [ProjectDocumentHelper saveProjectDocument:__DOC__]
    
    // Delegate
    
    - (void)chAddElementDetailViewController:(ChDetailViewController *)sender didPressSaveButton:(NSString *)message
    {
        SAVEDOC(THE_CURRENT_PROJECT_DOCUMENT);
    
        [self.navigationController popViewControllerAnimated:YES];
     }
    
    
    // Helper Class
    
    + (void)saveProjectDocument:(UIManagedDocument *)targetDocument
    {
        NSManagedObjectContext *moc = targetDocument.managedObjectContext;
        [moc performBlockAndWait:^{
            DLog(@" Process Pending Changes before saving : %@, Context = %@", targetDocument.description, moc);
    
            [moc processPendingChanges];
            [targetDocument saveToURL:targetDocument.fileURL forSaveOperation:UIDocumentSaveForOverwriting completionHandler:NULL];
        }];
    }
    
  7. デリゲート(ChElementTVC)がナビゲーションスタックからdetailViewをポップしたため、そのviewWillAppearが呼び出され、fetchedResultsController.delegateが復元されます。

    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
    
        if (!self.fetchedResultsController.delegate) {
    
            DLog(@"Sleep Now %@", self);
    
            //http://mobiledevelopertips.com/core-services/sleep-pause-or-block-a-thread.html
    
           [NSThread sleepForTimeInterval:0.1];
    
           DLog(@"Wake up %@", self);
    
           [self fetchedResultsControllerWithPredicate:_savedPredicate]; // App Hangs Here ... This is sending messages to CoreData objects.
    
           [self.tableView reloadData];
    }
    

アプリがないと[NSThread sleepForTimeInterval:0.1];ハングします。Xcodeを介してSIGINTを送信すると、デバッガーが取得され、次のことがわかります。

(lldb)bt

    * thread #1: tid = 0x1c03, 0x30e06054 libsystem_kernel.dylib semaphore_wait_trap + 8, stop reason = signal SIGINT
    frame #0: 0x30e06054 libsystem_kernel.dylib semaphore_wait_trap + 8
    frame #1: 0x32c614f4 libdispatch.dylib _dispatch_thread_semaphore_wait$VARIANT$mp + 12   
    frame #2: 0x32c5f6a4 libdispatch.dylib _dispatch_barrier_sync_f_slow + 92   
    frame #3: 0x32c5f61e libdispatch.dylib dispatch_barrier_sync_f$VARIANT$mp + 22
    frame #4: 0x32c5f266 libdispatch.dylib dispatch_sync_f$VARIANT$mp + 18
    frame #5: 0x35860564 CoreData _perform + 160

(lldb)フレーム選択5

    frame #5: 0x35860564 CoreData _perform + 160
    CoreData _perform + 160:
    -> 0x35860564:  add    sp, #12
       0x35860566:  pop    {r4, r5, r7, pc}

    CoreData -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]:
       0x35860568:  push   {r4, r5, r6, r7, lr}
       0x3586056a:  add    r7, sp, #12

(lldb)逆アセンブル-f

    CoreData _perform:
        0x358604c4:  push   {r4, r5, r7, lr}

    ... snipped ...

        0x35860560:  blx    0x35938bf4                ; symbol stub for: dispatch_sync_f

    -> 0x35860564:  add    sp, #12
       0x35860566:  pop    {r4, r5, r7, pc}

別の回避策が可能です。fetchedResultsController.delegate復元をコーディングする-[ChElementTVC viewDidAppear:]と、メインキューでのこの設定も効果的に遅延します。

追加の回避策は、プロジェクトの保存が完了した後、完了ブロックでnavpopを実行することです。

    #define SAVEDOCWITHCOMPLETION(__DOC__,__COMPLETION_BLOCK__)[ProjectDocumentHelper saveProjectDocument:__DOC__ completionHandler:__COMPLETION_BLOCK__]

    void (^completionBlock)(BOOL) = ^(BOOL success) {
        [self.navigationController popViewControllerAnimated:YES];
    };

    SAVEDOCWITHCOMPLETION(THE_CURRENT_PROJECT_DOCUMENT, completionBlock);

保存操作はメインキューでのデリゲートの復元と同時にバックグラウンドで実行されると思いますが、その理論を検証/証明/反証する方法がわかりません。

それで、誰かが何が起こっているのか、そしてこの状況でのベストプラクティスは何であるかを説明できますか?また、研究のための参考文献をいただければ幸いです。

4

1 に答える 1

1

最終的に3番目の方法を実装しました。つまり、CoreDataストアとやり取りするトランザクションをシリアル化するために、完了ブロックを使用してドキュメントを保存しました。

于 2012-10-01T20:17:38.557 に答える