UIManagedDocument单例代码openWithCompletionHandler调用两次并崩溃 [英] UIManagedDocument Singleton Code openWithCompletionHandler called twice and crashes

查看:98
本文介绍了UIManagedDocument单例代码openWithCompletionHandler调用两次并崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 iOS5.1:同步任务(等待完成),但是@sychronized在这种情况下不起作用,因为下面的performWithDocument是在同一线程上调用的.如何防止多次调用openwithCompletionHandler?我认为可以防止这种情况的唯一方法是暂停执行上面的调用之一,直到我确定UIDocumentStateNormal为true,然后再释放.那虽然会冻结主要的UI线程,但这并不好.在不冻结UI的情况下,最好的方法是什么?

I am using Justin Driscoll's implementaion on Core Data with a Single Shared UIManagedDocument. Everything was fine in my iphone app until I moved it to a iPad storyboard and a splitview controller for the ipad app. The problem is openwithCompletionHandler is being called twice, once from my master view in viewDidLoad and again in my detail view viewWillLoad. The calls are in quick succession and since the document is still in UIDocumentStateClosed when the second call is made to my performWithDocument method (below) of the singleton the app crashes. I looked at e_x_p ' s answer for post iOS5.1: synchronising tasks (wait for a completion) but @sychronized will not work in this case since performWithDocument below is called on the same thread. How would I protect against multiple calls to openwithCompletionHandler? The only way I can think to protect against this is to pause execution of one of the calls above until i am sure UIDocumentStateNormal is true and then release. That though would freeze the main UI thread which is not good. What though would be the best way todo this without freezing up the UI?

通过UIManagedDocumentSingleton代码:

From the UIManagedDocumentSingleton code:

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

推荐答案

我做到了,就像贾斯汀在下面的上面所建议的那样.在大约2万名用户中,我的一个应用程序可以在两年内正常运行.

I did it as Justin suggested above below. Works fine in one of my apps for two years with ~20k users.

@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];
    }
}

Swift (测试不多)

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

这篇关于UIManagedDocument单例代码openWithCompletionHandler调用两次并崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆