我使用的贾斯汀·德里斯科尔对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);
}
}
我这样做是因为贾斯汀上述以下建议。 在工作了两年,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)
}
}
这很有趣,肯定在我的代码中的缺陷(对不起!)。 我首先想到的是添加一个串行队列的属性,以您的文档处理类和执行上的检查。
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。
的,其之间共享代码块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