UIManagedDocument辛格尔顿代码openWithCompletionHandler打了

2019-08-03 00:13发布

我使用的贾斯汀·德里斯科尔对implementaion 核心数据与单个共享UIManagedDocument 。 一切都在我的iPhone应用程序正常,直到我把它移动到iPad的故事板和iPad应用程序一个SPLITVIEW控制器。 问题是openwithCompletionHandler在我的详细视图viewWillLoad被调用两次,从viewDidLoad中我的主视图一次试。 电话是在快的连续,并且由于文件仍处于UIDocumentStateClosed当第二个呼叫我performWithDocument方法单身的(下图)的应用程序崩溃制成。 我看着e_x_p的答案后iOS5.1:同步任务(等待完成),但由于低于performWithDocument被称为同一线程@sychronized不会在这种情况下工作。 我将如何防止多次调用openwithCompletionHandler? 我能想到的,以防止这种情况的唯一办法是,直到我相信UIDocumentStateNormal是真实的,然后松开暂停上述其中一个呼叫的执行。 ,尽管将冻结的主UI线程这是不好的。 但什么是待办事项这不结冰了UI的最佳方式?

从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);
    }
}

Answer 1:

我这样做是因为贾斯汀上述以下建议。 在工作了两年,20K〜我的用户应用程序之一的罚款。

@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)
}
}


Answer 2:

这很有趣,肯定在我的代码中的缺陷(对不起!)。 我首先想到的是添加一个串行队列的属性,以您的文档处理类和执行上的检查。

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

然后在performWithDocument:

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

但是,这将不会工作...

当你打电话saveToURL并在回调清除它,你可以设置一个BOOL标志。 然后,您可以检查该标志和使用performSelectorAfterDelay如果正在创建的文件再次待会儿打电话performWithDocument。



Answer 3:

的,其之间共享代码块numberOfRowsInSection:cellForRowAtIndexPath:应当仅调用一次。 numberOfRowsInSection的前总是会叫tableView试图使细胞,所以你应该创建一个NSArray渲染你的细胞时,你可以储存读取请求到的结果,然后使用这个数组对象:

@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的块(LA内设置__block )。

造成这种情况的主要原因是,你需要确保的数据集用于确定行#100%的时间是在大小,当你创建你的细胞相同。 如果他们不匹配,那么你就会崩溃。 此外,由于你没有一个块你不需要派遣作出UITableViewCell现在更新。

最后,如果UIDocumentStateClosed导致问题您应该筛选出来你的NSFetch结果(附加断言,看NSCompoundPredicate如果需要的话),或有一些代码来更好地处理它们cellForRowAtIndexPath:



文章来源: UIManagedDocument Singleton Code openWithCompletionHandler called twice and crashes