我已经在我的应用中实现了今天的视图扩展与CoreData共享,我有多个问题(比如只有一个对象显示我有三个)和一个大问题,"由于未被捕获而终止应用异常' NSObjectInaccessibleException',原因:' CoreData无法完成' 0xd0000000001c0004"的错误。现在,只有当我在后台打开应用程序时才会发生这种情况,这让我相信我的应用程序正在离开商店处于不良状态,但这确实不应该发生。我使用Persistent Stack,外部'库'管理https://gist.github.com/mluisbrown/7015953
上可读的所有CoreData(而不是AppDelegate)从TodayView扩展程序获取CoreData:
-(void)viewDidLoad{
self.persistentStack = [[PersistentStack alloc] initWithStoreURL:self.storeURL modelURL:self.modelURL];
self.managedObjectContext = self.persistentStack.managedObjectContext;
}
- (NSURL*)storeURL
{
//NSURL* documentsDirectory = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
// return [documentsDirectory URLByAppendingPathComponent:@"WhatIOwe.sqlite"];
NSURL *directory = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.bittank.io"];
NSURL *storeURL = [directory URLByAppendingPathComponent:@"WhatIOwe.sqlite"];
return storeURL;
}
- (NSURL*)modelURL
{
return [[NSBundle mainBundle] URLForResource:@"WhatIOwe" withExtension:@"momd"];
}
- (NSFetchedResultsController *)fetchedResultsController {
self.persistentStack = [[PersistentStack alloc] initWithStoreURL:self.storeURL modelURL:self.modelURL];
self.managedObjectContext = self.persistentStack.managedObjectContext;
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription
entityForName:@"OweInfo" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc]
initWithKey:@"details.date" ascending:NO];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController =
[[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:managedObjectContext sectionNameKeyPath:nil
cacheName:@"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
正如所建议的那样,我在持久堆栈中尝试了合并策略:
[self.managedObjectContext.persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:self.storeURL
options:@{ NSPersistentStoreUbiquitousContentNameKey : @"WhatIOwe",
NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES,
NSMergeByPropertyObjectTrumpMergePolicy : @YES}
error:&error];
关于配置我的NSManagedObjectContext的另一个观察,传递:
[psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:self.storeURL options:nil error:&error]; allows the extension to read the store (but still throw the error I'm having), but passing
[psc addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:self.storeURL options:@{ NSPersistentStoreUbiquitousContentNameKey : @"iCloudStore",
NSMigratePersistentStoresAutomaticallyOption : @YES,
NSInferMappingModelAutomaticallyOption : @YES,
} error:&error]; will result in the extension not reading any data whatsoever.
旁注:psc
作为
__weak NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator;
self.persistentStoreCoordinator = self.managedObjectContext.persistentStoreCoordinator;
答案 0 :(得分:1)
这里发生的是您有两个不同的进程访问同一个Core Data存储,每个进程都有自己的NSPersistentStoreCoordinator
。
一个进程修改了商店 - 很可能是删除。另一个进程无法知道发生了此删除,并且内存中已有一个指向现在已删除数据的对象。当该进程试图访问该对象时,它必须进入商店以获取不再存在的数据(“触发故障”)。
核心数据不是为此类用途而设计的,扩展功能与应用程序非常不同。如果您的扩展程序能够写入与应用程序相同的数据,您可以重新考虑您的方法,并使扩展程序只能读取数据,并且永远不会将数据保存在内存中很长时间。这至少可以缓解遇到这些问题的最常见方法。
答案 1 :(得分:1)
回复上述答案和问题: "核心数据并非设计用于此类用途"
完全是。评估是正确的:实际的应用程序中可能已删除某些内容,并且扩展程序无法识别。幸运的是CoreData提供了一种处理这种情况的方法。查看NSManagedObjectContext的stalenessInterval属性。它定义了内存缓存有多长时间。如果由于外部进程正在更改它们而导致内存缓存因磁盘存储更改而过时而导致出现问题,只需在扩展中将staleness时间间隔设置为0,这将告诉CoreData始终获取新值从商店中忽略内存缓存。
如果您持有对内存中对象的引用,并且该对象在商店中被删除,您仍然可能会遇到问题,因此请务必检查以确保您正在访问的对象尚未被删除。 / p>
如果您想获得更多细节,还有其他一些选择。您可以在保存内容时将主应用程序的通知发送到您的扩展程序,以提供重新加载数据的手动触发器。您还可以发送已删除或修改的特定对象ID,并使用refreshObject ...方法手动刷新。或者查看mergeChangesFromContextDidSaveNotification:。您可以手动序列化期望的字典,然后将其发送出去。
您还可以让父应用处理所有数据库访问并通过通知发回结果。这可能是不必要的。
所有这些都需要一些工作,但你会遇到任何问题 数据库系统,其中跨多个进程访问数据库,并且存在缓存。
答案 2 :(得分:1)
您的代码存在多个问题,可能会导致核心数据状态混乱。我无法确定它们是否会导致您的问题,但目前事情已经非常糟糕,以至于尝试调试此特定错误正在领先于事情。这些问题包括:
对您在应用和扩展程序之间共享数据的方式感到困惑。
您可以使用iCloud执行此操作,也可以使用应用程序组直接共享持久存储。您似乎正在尝试这两种方式,这不仅是不必要的,而且可能会导致延长更新的问题。
如果您使用iCloud 分享数据,则不需要应用程序组,因为应用程序和扩展程序都将从iCloud获取数据。在这种情况下,您不共享本地持久性存储,而是通过iCloud传输数据。
如果您使用应用程序组来共享持久存储文件,则无需使用iCloud。应用程序和扩展程序都访问位于组容器中的同一文件。每个都可以读写。
冲突的iCloud设置。您在不同的地方使用NSPersistentStoreUbiquitousContentNameKey
的不同值。即使假设iCloud工作正常,这也会阻止您共享数据。如果应用和扩展程序将通过iCloud共享,则他们需要访问同一个云存储,但您似乎指示他们使用单独的云数据存储。
您实际上并未使用您所针对的合并政策。在添加持久性存储文件时,您将NSMergeByPropertyObjectTrumpMergePolicy
作为其中一个选项传递,但这实际上并不是一个有效的选项。我本来期望至少有关于此的控制台消息,但如果没有,那么这意味着Core Data只是默默地忽略该密钥。您可以通过在托管对象上下文中设置mergePolicy
属性的值来设置合并策略。如果没有合并政策,您将退回到默认NSErrorMergePolicy
。
添加持久存储时不寻常的可疑代码。在大多数使用Core Data的情况下,您需要添加持久性存储,然后再创建一个或多个托管对象上下文。您似乎首先创建上下文,然后才添加持久性存储。这不是必然错误,但这是非常不寻常的。如果我正在处理这段代码,那么仔细查看Core Data堆栈的生命周期将是一个红旗。特别是如果您的代码注释表明,在某些情况下您根本没有获得任何数据。
同样,我不确定上述内容是否会直接导致此特定错误,但它们会导致各种类型的问题,如果由此产生的混淆状态导致此错误也不会令人感到意外。