1

次のメソッドを持つシングルトンクラス(DTTSingleton)があります。

+ (UIManagedDocument *)managedDocument
{    
    static UIManagedDocument *managedDocument = nil;
    static dispatch_once_t mngddoc;

    dispatch_once(&mngddoc, ^
    {
        if(!managedDocument)
        {
            NSURL *url = [[DTTHelper applicationDocumentsDirectory] URLByAppendingPathComponent:kDTTDatabaseName];
            managedDocument = [[DTTManagedDocument alloc] initWithFileURL:url];            
        }
    });

    return managedDocument;
}

+ (void)useDefaultDocumentWithBlock:(completion_block_t)completionBlock
{
    if (![[NSFileManager defaultManager] fileExistsAtPath:[DTTSingleton.managedDocument.fileURL path]])
    {
        [DTTSingleton.managedDocument saveToURL:DTTSingleton.managedDocument.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success)
         {
             if (success)
             {
                 completionBlock(DTTSingleton.managedDocument.managedObjectContext);                 
             }
             else
             {
                 NSLog(@"Failed to save!");
             }
         }];
    }
    else if (DTTSingleton.managedDocument.documentState == UIDocumentStateClosed)
    {
        [DTTSingleton.managedDocument openWithCompletionHandler:^(BOOL success)
         {
             if (success)
             {
                 completionBlock(DTTSingleton.managedDocument.managedObjectContext);
             }
             else
             {
                 NSLog(@"Failed to open!");
             }
         }];
    }
    else if (DTTSingleton.managedDocument.documentState == UIDocumentStateNormal)
    {
        completionBlock(DTTSingleton.managedDocument.managedObjectContext);
    }
}

そして、私のUITableViewControllerには、viewDidLoadメソッドに次のコードがあります。

   [DTTSingleton useDefaultDocumentWithBlock:^(NSManagedObjectContext *moc)
    {
        NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"SomeEntity"];
        self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:moc cacheName:nil];
    }];
    [DTTSingleton useDefaultDocumentWithBlock:^(NSManagedObjectContext *moc)
     {
         NSLog(@"When this is called it errors because DTTSingleton is already trying to open it!");
     }];

実行すると、次のエラーが発生します。

キャッチされなかった例外'NSInternalInconsistencyException'が原因でアプリを終了しました、理由:'すでに開いているか、元に戻す操作が実行中のドキュメントを開こうとしたか、元に戻しました

このエラーが発生する理由を理解しています。これは、別の開くプロセスがすでに実行されているときにドキュメントを開こうとしているためです。だから私の質問は...

1)openWithCompletionHandlerが1回だけ呼び出されるようにするにはどうすればよいですか?

2)ドキュメントが開いたら、2番目のブロックが確実に実行されるようにするにはどうすればよいですか?

助けてくれてありがとう!

4

2 に答える 2

4

あなたがまだこれを見たかどうかはわかりませんが、おそらく良いリソースがここにあります: http://adevelopingstory.com/blog/2012/03/core-data-with-a-single-shared-uimanageddocument. html

あなたが(上記のリンクを使用するのではなく)独自のものを作成しようとしていて、何らかの入力を探している場合は、私が見たいくつかのことを指摘できます(ただし、私は決して主張しません専門家であること)...

とにかく、あなたの問題はここにあると思います:

// ....
} else if(DTTSingleton.managedDocument.documentState == UIDocumentStateClosed) {

    [DTTSingleton.managedDocument openWithCompletionHandler:^(BOOL success) {
         if(success) {
             completionBlock(DTTSingleton.managedDocument.managedObjectContext);
         } else {
             NSLog(@"Failed to open!");
         }
     }];

}

メソッドopenWithCompletionHandlerは、ドキュメントへの接続を非同期で開こうとします。これに関する問題は、UITableView でドキュメントを開くための最初の呼び出しで、使用しているコードがドキュメントが閉じていることを通知するため、ドキュメントを開こうとすることです。これはすべてうまくいきますが、ここで使用しているコードは、シングルトンのインスタンスを作成するためのさらに別の試みを再発行します。おそらく、これは非常に高速に (そして接近して) 発生しているため、ドキュメントを非同期で開こうとしています。

これをテストするには、次の行の UIDocumentStateClosed チェックの後にブレークポイントを配置してみてください。

[DTTSingleton.managedDocument openWithCompletionHandler:^(BOOL success)

これが何度も実行されるのを見ると思います...

私はこれを解決する方法を説明するほど熟練していませんが、上記のリンクに示されているアプローチを使用して、ブロックを適用して開いているドキュメントの存在を割り当て/追跡することを真剣にお勧めします

うまくいけば、それは役に立ちますか?

編集: * ブレークポイントの提案を追加しました。

編集: 同様の問題(および提案/結論)を伴う別のstackoverflowチケットがあります-だから、結局ここからそれほど遠くないかもしれません=)

于 2013-03-12T03:48:26.660 に答える
1

ドキュメントの準備が整うまでコアデータにアクセスするボタンを無効にすることで、私が抱えていた問題が解決されたと言って投稿したいと思いました。これにより、別のプロセスと同時にドキュメントを開こうとすることはありません。また、viewDidLoad のようなライフ サイクル ハンドラでのコア データ アクセスについては、ドキュメントが開いている場合 (状態を変数で手動で保持)、ドキュメントが開くまでループして呼び出しを遅らせるシステムを実装しました。つまり、呼び出しをキューに入れます。 . ループ内の呼び出しで performSelector:withObject:afterDelay: を使用することを忘れないでください。そうしないと、アプリケーションがクラッシュします。

あなたの提案をありがとうジョン。

于 2013-04-17T11:25:50.030 に答える