我在iOS项目中使用Core Data。
我以下列方式使用多个上下文。我有一个persisent store context
,它在私有队列上运行,并存储对持久存储的更改。
我有一个main queue context
是persistent store context
的孩子。我的应用中的所有FRC
都使用此上下文。
最后,如果我要进行一些我想要批量保存的更改,我会创建并使用新的NSManagedObjectContext
s,它们是主队列上下文的子项。
所以我有一个链:
DB < persistent store context < main queue context < any other child contexts
我有一个保留persistent store context
和main queue context
的单身人士。这个单例还会监听NSManagedObjectContextObjectsDidChangeNotification
上的persistent store context
通知,并对此通知做出反应:
-(void) persistentStoreContextDidChangeNotification:(NSNotification*)notification
{
if (notification.object == self.persistentStoreContext) {
[self.persistentStoreContext performBlockAndWait:^{
//on every change on persistentStoreContext, save those changes using background thread to real persistent store
NSError *error = nil;
[self.persistentStoreContext save:&error];
}];
}
}
因此,当我致电[mainQueueContext save]
时,这足以让mainQueueContext
中的所有更改都保留在文件系统中。
使用Crashlytics,我收到了很多这样的崩溃报告:
Fatal Exception: NSInternalInconsistencyException
Can't create externalDataReference interim file : 28
Thread : Fatal Exception: NSInternalInconsistencyException
0 CoreFoundation 0x0000000183f51e48 __exceptionPreprocess + 132
1 libobjc.A.dylib 0x000000019464c0e4 objc_exception_throw + 60
2 CoreData 0x0000000183c6e5b4 +[_PFRoutines writePFExternalReferenceDataToInterimFile:] + 960
3 CoreData 0x0000000183ceaa4c -[NSSQLCore writeExternalDataReferences] + 224
4 CoreData 0x0000000183c429fc -[NSSQLCore saveChanges:] + 596
5 CoreData 0x0000000183c0b078 -[NSSQLCore executeRequest:withContext:error:] + 720
6 CoreData 0x0000000183cd2254 __65-[NSPersistentStoreCoordinator executeRequest:withContext:error:]_block_invoke + 4052
7 CoreData 0x0000000183cd9654 gutsOfBlockToNSPersistentStoreCoordinatorPerform + 180
8 libdispatch.dylib 0x0000000194c9136c _dispatch_client_callout + 16
9 libdispatch.dylib 0x0000000194c9a6e8 _dispatch_barrier_sync_f_invoke + 76
10 CoreData 0x0000000183ccccb4 _perform + 180
11 CoreData 0x0000000183c0ac34 -[NSPersistentStoreCoordinator executeRequest:withContext:error:] + 300
12 CoreData 0x0000000183c31400 -[NSManagedObjectContext save:] + 1284
13 MyApp 0x00000001000d17e0 __58-[XEECDStack persistentStoreContextDidChangeNotification:]_block_invoke (XEECDStack.m:426)
14 CoreData 0x0000000183ca5270 developerSubmittedBlockToNSManagedObjectContextPerform + 200
15 CoreData 0x0000000183ca5474 -[NSManagedObjectContext performBlockAndWait:] + 232
16 MyApp 0x00000001000d1774 -[XEECDStack persistentStoreContextDidChangeNotification:] (XEECDStack.m:423)
17 CoreFoundation 0x0000000183ef81e0 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20
18 CoreFoundation 0x0000000183e37370 _CFXNotificationPost + 2060
19 Foundation 0x0000000184d32cc0 -[NSNotificationCenter postNotificationName:object:userInfo:] + 72
20 CoreData 0x0000000183c33d14 -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] + 364
21 CoreData 0x0000000183c321bc -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 2552
22 CoreData 0x0000000183cadba4 -[NSManagedObjectContext(_NestedContextSupport) _parentProcessSaveRequest:inContext:error:] + 1568
23 CoreData 0x0000000183cae684 __82-[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:]_block_invoke + 600
24 CoreData 0x0000000183cb0398 internalBlockToNSManagedObjectContextPerform + 108
25 libdispatch.dylib 0x0000000194c9136c _dispatch_client_callout + 16
26 libdispatch.dylib 0x0000000194c9a6e8 _dispatch_barrier_sync_f_invoke + 76
27 CoreData 0x0000000183ca06cc _perform + 208
28 CoreData 0x0000000183cae354 -[NSManagedObjectContext(_NestedContextSupport) executeRequest:withContext:error:] + 176
29 CoreData 0x0000000183c31400 -[NSManagedObjectContext save:] + 1284
30 MyApp 0x000000010012ea14 __78-[MyAppManager downloadMediaIfNeededForMyAppWithManagedObjectID:onCompletion:]_block_invoke (MyAppManager.m:415)
31 MyApp 0x00000001001ce06c __38-[FLNMyAppMediaDownloadOperation main]_block_invoke60 (FLNMyAppMediaDownloadOperation.m:84)
32 Foundation 0x0000000184e07508 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 16
33 Foundation 0x0000000184d58c94 -[NSBlockOperation main] + 96
34 Foundation 0x0000000184d4861c -[__NSOperationInternal _start:] + 636
35 Foundation 0x0000000184e0a26c __NSOQSchedule_f + 228
36 libdispatch.dylib 0x0000000194c9136c _dispatch_client_callout + 16
37 libdispatch.dylib 0x0000000194c95980 _dispatch_main_queue_callback_4CF + 932
38 CoreFoundation 0x0000000183f096a0 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 12
39 CoreFoundation 0x0000000183f07748 __CFRunLoopRun + 1492
40 CoreFoundation 0x0000000183e351f4 CFRunLoopRunSpecific + 396
41 GraphicsServices 0x000000018cfcb5a4 GSEventRunModal + 168
42 UIKit 0x0000000188766784 UIApplicationMain + 1488
43 MyApp 0x0000000100119cc8 main (main.m:17)
44 libdyld.dylib 0x0000000194cbaa08 start + 4
所以,在这个特定的崩溃中,行
30 MyApp 0x000000010012ea14 __78-[MyAppManager downloadMediaIfNeededForMyAppWithManagedObjectID:onCompletion:]_block_invoke (MyAppManager.m:415)
正在调用[mainQueueContext save]
。
在从应用程序的不同部分调用[mainQueueContext save]
的情况下会发生同样的崩溃。
我试图找出为什么我看到这个例外的可能原因。在搜索"Can't create externalDataReference interim file"
修改
我已经完成了特定的崩溃实例并逐一查看了它们。尽管每次崩溃的堆栈跟踪都非常相似,但事实证明异常在实例之间略有不同。
此次崩溃的不同版本是:
Fatal Exception: NSInternalInconsistencyException
External data reference can't find underlying file.
,
Fatal Exception: NSInternalInconsistencyException
Missing bytes from file at path /private/var/mobile/Containers/Data/Application/E9D51467-7941-41B8-88EE-31A70A82BC40/tmp/.LINKS/BD171390-D95C-459E-96D6-462318016138/EDB75A4F-C8FD-4BC4-ABD1-BA408F3A7DC9_0x18038c80, expected 276428, got 4294967295
,
Fatal Exception: NSInvalidArgumentException
Unable to open file with path: /var/mobile/Containers/Data/Application/19E53BC4-7891-4B0C-9454-27C1A0DAB2A0/Documents/persistent-store/.Model_SUPPORT/_EXTERNAL_DATA/CA6BCE7D-0C74-41CF-8784-8EB1F66DFF4C (13)
,
Fatal Exception: NSInternalInconsistencyException
This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation.
此外,我发现了几乎所有此类崩溃共享的模式。 设备上的可用磁盘空间非常低。在iPhone 4和iPhone 5设备上,可用磁盘空间为0%。在iPhone 6上,它总是8%。
我怀疑iPhone 6为特殊场合预留了8%的磁盘空间。
答案 0 :(得分:1)
我在我的应用程序中使用了类似的过程,在构建核心数据堆栈期间设置了两个上下文并使用:
NSPrivateQueueConcurrencyType
,NSMainQueueConcurrencyType
。为了猜测,这些块可能与通知的操作有冲突,但这是猜测。
对于我的应用程序,我修改了Marcus Zarra在他的书“The Pragmatic Bookshelf”中提出的解决方案 - “核心数据,第2版,iOS,OS X和iCloud的数据存储和管理”(2013年1月)。 / p>
有关我尚未实施的更新,最全面的解决方案,请阅读Zarra撰写的这篇文章My Core Data Stack。
我修改过的解决方案涉及编写一个与我的核心数据堆栈(在同一类中)一起构建的自定义保存方法,如下所示,而不是使用NSManagedObjectContextObjectsDidChangeNotification
。请注意,在代码中的适当位置调用save方法。
也许这是一个合适的选择?
属性...
@property (nonatomic, strong) NSManagedObjectContext *mocPrivate;
@property (nonatomic, strong) NSManagedObjectContext *mocMain;
自定义保存方法......
- (void)saveContextAndWait:(BOOL)wait {
if ([self.mocMain hasChanges]) {
[self.mocMain performBlockAndWait:^{
NSError __autoreleasing *error;
BOOL success;
if (!(success = [self.mocMain save:&error]))
NSLog(@"%@ - %@ - CORE DATA - E~R~R~O~R saving managedObjectContext MAIN: %@, %@", NSStringFromClass(self.class), NSStringFromSelector(_cmd), error.localizedDescription, error.localizedFailureReason);
NSLog(@"%@ - %@ - CORE DATA - Success saving managedObjectContext MAIN?: %@", NSStringFromClass(self.class), NSStringFromSelector(_cmd), success ? @"YES_" : @"NO_");
}];
}
void (^savePrivate) (void) = ^{
NSError __autoreleasing *error;
BOOL success;
if (!(success = [self.mocPrivate save:&error]))
NSLog(@"%@ - %@ - CORE DATA - E~R~R~O~R saving managedObjectContext PRIVATE: %@, %@", NSStringFromClass(self.class), NSStringFromSelector(_cmd), error.localizedDescription, error.localizedFailureReason);
NSLog(@"%@ - %@ - CORE DATA - Success saving managedObjectContext PRIVATE?: %@", NSStringFromClass(self.class), NSStringFromSelector(_cmd), success ? @"YES_" : @"NO_");
};
if ([self.mocPrivate hasChanges]) {
if (wait) {
[self.mocPrivate performBlockAndWait:savePrivate];
} else {
[self.mocPrivate performBlock:savePrivate];
}
}
}
答案 1 :(得分:0)
我设法通过填满设备上的整个磁盘空间然后使用应用程序将媒体存储在Core Data中来重现此问题。
使用该应用程序一段时间后,警告弹出窗口显示设备磁盘空间不足。我忽略了警告并继续使用该应用程序。 经过一段时间后,应用程序崩溃并显示以下消息:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Can't create externalDataReference interim file : 28'