2012-11-20 13 views
6

私はCore Data with a Single Shared UIManagedDocumentにJustin Driscollの実装を使用しています。 iPadのストーリーボードとipadアプリのsplitviewコントローラーに移動するまでは、すべて私のiPhoneアプリでうまくいきました。問題はopenwithCompletionHandlerがviewDidLoadのマスタービューから2回、詳細ビューviewWillLoadで2回呼び出されていることです。呼び出しはすぐに連続しています。また、ドキュメントがUIDocumentStateClosedにあるため、シングルトンのperformWithDocumentメソッド(2番目の呼び出し)が呼び出されると、アプリケーションがクラッシュします。投稿iOS5.1: synchronising tasks (wait for a completion)のe_x_pの答えを見ましたが、performWithDocumentが同じスレッド上で呼び出されているので、この場合は@sychronizedは動作しません。 openwithCompletionHandlerへの複数回の呼び出しを防ぐにはどうすればよいですか?私がこれを防ぐために考えることができる唯一の方法は、UIDocumentStateNormalがtrueであることを確認してから解放するまで、上記の呼び出しの実行を一時停止することです。しかし、それは良いではないメインのUIスレッドをフリーズします。 UIをフリーズすることなくこれをやりなおす最善の方法はありますか?UIManagedDocumentシングルトンコードopenWithCompletionHandlerが2回呼び出されてクラッシュする

UIManagedDocumentSingletonコードから:

- (void)performWithDocument:(OnDocumentReady)onDocumentReady 
{ 
    void (^OnDocumentDidLoad)(BOOL) = ^(BOOL success) 
    { 
     onDocumentReady(self.document); 
    }; 

    if (![[NSFileManager defaultManager] fileExistsAtPath:[self.document.fileURL path]]) 
    { 
     //This should never happen******************* 
     [self.document saveToURL:self.document.fileURL 
       forSaveOperation:UIDocumentSaveForCreating 
       completionHandler:OnDocumentDidLoad]; 

    } else if (self.document.documentState == UIDocumentStateClosed) { 
     [self.document openWithCompletionHandler:OnDocumentDidLoad]; 
    } else if (self.document.documentState == UIDocumentStateNormal) { 
     OnDocumentDidLoad(YES); 
    } 
} 

答えて

0

numberOfRowsInSection:cellForRowAtIndexPath:との間で共有されるコードのブロックが一度だけ呼び出されるべきです。あなたがにフェッチ要求の結果を格納した後、あなたの細胞をレンダリングするときに、この配列を使用することができますNSArrayオブジェクトを作成する必要がありますのでnumberOfRowsInSectionは常に、細胞をレンダリングするためにtableView試行する前に呼び出されます。

@implementation FooTableViewController { 
    NSArray *_privateArray; 
} 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    [[UIManagedDocumentSingletonHandler sharedDocumentHandler] performWithDocument:^(FCUIManagedDocumentObject *document) { 
     NSManagedObjectContext * context = document.managedObjectContext; 

     NSFetchRequest * request = [NSFetchRequest fetchRequestWithEntityName:@"FCObject"]; 
     NSPredicate * searchStringPredicate = nil; 
     if (searchFilterString) 
     { 
      searchStringPredicate = [NSPredicate predicateWithFormat:@"word BEGINSWITH[c] %@",searchFilterString]; 
     } 
     request.predicate = searchStringPredicate; 
     request.shouldRefreshRefetchedObjects = YES; 
     NSError * error; 
     _privateArray = [context executeFetchRequest:request error:&error]; 
     }]; 
    return _privateArray.count; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"FCCell"; 
    FCardCell *cell = (FCCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 

    // Configure the cell... 
    FCManagedObject * fcc = [_privateArray objectAtIndex:indexPath.row]; 
    cell.isWordVisible.on = fcc.isUsed; 
    cell.fWord.text = fcc.word; 
    return cell; 
} 

ブロック内で設定するには、NSArrayで特別なことをする必要がある場合は、私の頭の上からわからない(aaa __block)。

主な理由は、データセットが行の数を決定するために使用した時間の100%が、セルを作成するときと同じサイズであることを確認する必要があることです。彼らが一致しない場合、あなたはクラッシュします。またブロックを持っていないので、今すぐにUITableViewCellのアップデートを行うために発送する必要はありません。

最後UIDocumentStateClosedは面白いです、あなたがcellForRowAtIndexPath:

+0

私はcellForRowAtIndexPathとnumberOfRowsInSectionは今ので、使用単一最初のコールにフェッチ要求を集約しているもうそこに問題はありませんが、問題はまだ存在する。私はそれを簡素化するために問題を更新しました。 –

+0

あなたは '@synchronized(documentObject)'を使ってオブジェクト自体を同期させることができますか?それは、マスター/ディテールから権威を委譲して、そのうちの1人だけが呼び出しを行う必要がある(または、実行順序が保証されていれば、それらの間で( '@ protocol'を介して)メッセージを渡すことができます –

1

であなたのNSFetch結果からそれらをフィルタリング(追加の述語、必要に応じてNSCompoundPredicateを参照)、またはそれらをよりよく処理するコードを持っていなければならないのいずれかの問題を引き起こし、間違いなく私の欠陥されている場合コード(申し訳ありません!)私の最初の考えは、ドキュメント・ハンドラ・クラスにシリアル・キューをプロパティとして追加し、そのチェックを実行することです。 performWithDocumentで

self.queue = dispatch_queue_create("com.myapp.DocumentQueue", NULL); 

、その後:

dispatch_async(self.queue, ^{ 
    if (![[NSFileManager defaultManager] fileExistsAtPath... // and so on 
}); 

しかし、それはあなたがsaveToURLを呼び出し、コールバックでそれをクリアしたときに...

あなたはBOOLフラグを設定することができますいずれかの動作しません。その後、そのフラグをチェックして、performSelectorAfterDelayを使用して、ファイルが作成されているときにperformWithDocumentをもう一度呼び出すことができます。

2

私はジャスティンが より上に を提案したようにしました。 〜20kユーザーで2年間、自分のアプリケーションの1つでうまく動作します。

@interface SharedUIManagedDocument() 
@property (nonatomic)BOOL preparingDocument; 
@end 

- (void)performWithDocument:(OnDocumentReady)onDocumentReady 
{ 
    void (^OnDocumentDidLoad)(BOOL) = ^(BOOL success) { 
     onDocumentReady(self.document); 
     self.preparingDocument = NO; // release in completion handler 
    }; 

    if(!self.preparingDocument) { 
     self.preparingDocument = YES; // "lock", so no one else enter here 
     if(![[NSFileManager defaultManager] fileExistsAtPath:[self.document.fileURL path]]) { 
      [self.document saveToURL:self.document.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:OnDocumentDidLoad]; 
     } else if (self.document.documentState == UIDocumentStateClosed) { 
      [self.document openWithCompletionHandler:OnDocumentDidLoad]; 
     } else if (self.document.documentState == UIDocumentStateNormal) { 
      OnDocumentDidLoad(YES); 
     } 
    } else { 
     // try until document is ready (opened or created by some other call) 
     [self performSelector:@selector(performWithDocument:) withObject:onDocumentReady afterDelay:0.5]; 
    } 
} 

スウィフト(あまりテストされていない)

typealias OnDocumentReady = (UIManagedDocument) ->() 

class SharedManagedDocument { 

private let document: UIManagedDocument 
private var preparingDocument: Bool 

static let sharedDocument = SharedManagedDocument() 

init() { 
    let fileManager = NSFileManager.defaultManager() 
    let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) 
    let documentsDirectory: NSURL = urls.first as! NSURL 
    let databaseURL = documentsDirectory.URLByAppendingPathComponent(".database") 
    document = UIManagedDocument(fileURL: databaseURL) 
    let options = [NSMigratePersistentStoresAutomaticallyOption : true, NSInferMappingModelAutomaticallyOption : true] 
    document.persistentStoreOptions = options 
    preparingDocument = false 
} 

func performWithDocument(onDocumentReady: OnDocumentReady) { 

    let onDocumentDidLoad:(Bool) ->() = { 
     success in 
     onDocumentReady(self.document) 
     self.preparingDocument = false 
    } 
    if !preparingDocument { 
     preparingDocument = true 
     if !NSFileManager.defaultManager().fileExistsAtPath(document.fileURL.path!) { 
      println("Saving document for first time") 
      document.saveToURL(document.fileURL, forSaveOperation: .ForCreating, completionHandler: onDocumentDidLoad) 
     } else if document.documentState == .Closed { 
      println("Document closed, opening...") 
      document.openWithCompletionHandler(onDocumentDidLoad) 
     } else if document.documentState == .Normal { 
      println("Opening document...") 
      onDocumentDidLoad(true) 
     } else if document.documentState == .SavingError { 
      println("Document saving error") 
     } else if document.documentState == .EditingDisabled { 
      println("Document editing disabled") 
     } 
    } else { 
     // wait until document is ready (opened or created by some other call) 
     println("Delaying...") 
     delay(0.5, closure: { 
      self.performWithDocument(onDocumentReady) 
     }) 
    } 
} 

private func delay(delay:Double, closure:()->()) { 
    dispatch_after(
     dispatch_time(
      DISPATCH_TIME_NOW, 
      Int64(delay * Double(NSEC_PER_SEC)) 
     ), 
     dispatch_get_main_queue(), closure) 
} 
} 
+1

こんにちは、これをスペルアウトしてくれてありがとうVladimir! – Corey

+0

"//"ロックするので誰もここには誰も入れません " - それは非常に大きな間違いです。 – adnako

+0

@adnakoもしあなたが言語学を話しているなら、私は "ロック"はそれが単なる旗である限り、ここで良い言葉ではないと同意します。しかし、このアプローチに弱い側面があると説明してください、私は本当に興味があります! –

関連する問題