Ios NSFetchedResults在保存持久MOC后返回旧数据 上下文
我有一个简单的核心数据堆栈:Ios NSFetchedResults在保存持久MOC后返回旧数据 上下文,ios,core-data,Ios,Core Data,我有一个简单的核心数据堆栈:MainQueueMOC->PrivateBackgroundMOC->persistentstorecordinator,它由我的TTPersistenceManager管理,如下所示: typedef NS_ENUM(NSInteger, TTPersistenceType) { TTPersistenceTypeInMemory, TTPersistenceTypeSQLite }; @interface TTPersistenceManage
MainQueueMOC->PrivateBackgroundMOC->persistentstorecordinator
,它由我的TTPersistenceManager
管理,如下所示:
typedef NS_ENUM(NSInteger, TTPersistenceType) {
TTPersistenceTypeInMemory,
TTPersistenceTypeSQLite
};
@interface TTPersistenceManager : NSObject
@property (strong, nonatomic, readonly) NSManagedObjectContext *managedObjectContext; // this is the MainQueueMOC
- (id)initWithPersistenceType:(TTPersistenceType)persistenceType;
- (void)initializeCoreData;
- (void)save;
- (void)persist;
item.kind = "relationship" AND item.relationship.archived == NO
目前我们只使用内存存储。
它的灵感来自于此。因此,MainQueueMOC是唯一的真相来源,PrivateBackgroundMOC只用于在后台保存到存储中,并且从不公开。如果您阅读本文,您会注意到我添加了一个名为persist
的方法,save
和persist
之间的区别是:
,使用save
performBlockAndWait
,使用persist
保存主队列MOC,使用performBlock和wait
performBlock
save
是保存单一真相来源的方法,我们在对托管对象进行任何更改后调用它,而persist
仅在应用程序委托转到后台或即将终止时调用,以保存对存储的所有更改
除以下问题外,此操作正常
问题
我们有一个NSFetchedResultsController
,其谓词如下:
typedef NS_ENUM(NSInteger, TTPersistenceType) {
TTPersistenceTypeInMemory,
TTPersistenceTypeSQLite
};
@interface TTPersistenceManager : NSObject
@property (strong, nonatomic, readonly) NSManagedObjectContext *managedObjectContext; // this is the MainQueueMOC
- (id)initWithPersistenceType:(TTPersistenceType)persistenceType;
- (void)initializeCoreData;
- (void)save;
- (void)persist;
item.kind = "relationship" AND item.relationship.archived == NO
我们不显示与存档关系关联的项目。用户可以滑动该行对其进行存档,这会更改关系。存档=@YES
,调用[TTPersistenceManager save]
,然后重新提取NSFetchedResultsController
,该项将从列表中消失。这很有效
直到我们第一次进入后台
- (void)applicationDidEnterBackground:(UIApplication *)application {
[self.persistenceController persist];
}
调用persist
后,如果用户滑动以存档项目,则该项目不会从列表中消失
relationship.archived = @YES; // item at index 0 is associated with this object
[self.persistenceManager save];
[self.fetchedResultsController performFetch:&error]; // Works, no error
item = [[self.fetchedResultsController] fetchedObjects] firstObject];
NSLog(@"item is archived %d", item.relationship.archived);
// prints: item is archived 1
关系
对象的存档
属性设置为是
,但提取仍然返回它
可能的解决办法
我找到了两种可能的解决办法。但是我想选择一个更正确的,无论我们使用内存存储还是sqlite,在所有情况下都能正常工作的
\一,。将updatedAt
属性添加到项目
,我们在每次更新关系时设置当前日期
:
relationship.item.updatedAt = [NSDate date];
relationship.archived = @YES;
[self.persistenceManager save];
\二,。始终调用persist
而不是save
:
relationship.archived = @YES;
[self.persistenceManager persist];
正确的方法是什么
我的假设是,在进入后台时,我们只保存连接到持久存储的MOC吗
为什么向项
添加一个谓词中甚至没有使用的属性会起作用
更新:源代码
@implementation TTPersistenceManager
- (id)initWithPersistenceType:(TTPersistenceType)persistenceType {
self = [super init];
if (self) {
_persistenceType = persistenceType;
}
return self;
}
- (void)initializeCoreData {
FCYAssert(!self.managedObjectModel, @"CoreData has already been initialized");
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
self.managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:@[bundle]];
self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
NSError *error = nil;
NSPersistentStore *store = [self.persistentStoreCoordinator addPersistentStoreWithType:[self storageType] configuration:nil URL:nil options:nil error:&error];
FCYAssert(store != nil, @"Failed create persistent store: %@\n%@", [error localizedDescription], [error userInfo]);
self.persistentStoreManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
self.persistentStoreManagedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
self.persistentStoreManagedObjectContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
self.managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.managedObjectContext.parentContext = self.persistentStoreManagedObjectContext;
self.managedObjectContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
}
- (void)persist {
if (![self saveContext:self.managedObjectContext]) return;
[self saveContext:self.persistentStoreManagedObjectContext];
}
- (void)save {
[self saveContext:self.managedObjectContext];
}
#pragma mark - Private
- (NSString *)storageType {
if (self.persistenceType == TTPersistenceTypeSQLite) return NSSQLiteStoreType;
return NSInMemoryStoreType;
}
- (BOOL)saveContext:(NSManagedObjectContext *)context {
NSError *error = nil;
BOOL didSave = [self saveContext:context error:&error];
if (!didSave) {
TTLogError(@"Error saving context: %@\n\nUser Info:\n%@\n\nCall Stack:\n%@", error.localizedDescription, error.userInfo, [NSThread callStackSymbols]);
}
return didSave;
}
- (BOOL)saveContext:(NSManagedObjectContext *)context error:(NSError **)errorPtr {
__block BOOL hasChanges = NO;
[context performBlockAndWait:^{
hasChanges = [context hasChanges];
}];
if (!hasChanges) return YES;
__block NSError *error = nil;
__block BOOL didSave = NO;
[context performBlockAndWait:^{
didSave = [context save:&error];
}];
if (!didSave && error && errorPtr) {
*errorPtr = error;
}
return didSave;
}
@end
NSFetchedResultsController
- (NSFetchedResultsController *)setupFetchedResultsController {
if (!_fetchedResultsController) {
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:[TTInboxItem entityName]];
fetchRequest.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:TTInboxItemAttributes.sortDate ascending:NO]];
NSPredicate *itemPredicate = [NSPredicate predicateWithFormat:@"%K == %@", TTInboxItemAttributes.type, [TTRelationship entityName]];
NSPredicate *notArchivedPredicate = [NSPredicate predicateWithFormat:@"%K.%K != %@", TTInboxItemRelationships.relationship, TTRelationshipAttributes.archived, @YES];
NSPredicate *notArchivedPredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[itemPredicate, notArchivedPredicate]];
fetchRequest.predicate = notArchivedPredicate;
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.persistenceManager.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
self.fetchedResultsController.delegate = self.tableViewBatchUpdater;
[self fetchInboxItems];
}
return _fetchedResultsController;
}
我曾经在我的一个应用程序中遇到过类似的问题。文档中的这个提示帮助我找到了解决方案 来自苹果: 如果上下文的父存储是持久存储协调器,则更改将提交到外部存储。如果上下文的父存储是另一个托管对象上下文,则“保存:仅更新该父存储中的托管对象”。若要将更改提交到外部存储,必须将上下文链中的更改保存到父级为持久存储协调器的上下文(包括该上下文)中 由于您具有此结构
MainQueueMOC->PrivateBackgroundMOC->persistentstorecordinator
,因此您应该尝试获取上下文并执行类似于[TTPersistenceManager.managedObjectContext save]
的操作。如果有多个上下文,可以创建一个调用save和上下文的方法
至于“我的假设是,当进入后台时,我们只保存连接到持久存储的MOC吗?”您可以保存您想要的内容,但始终要检查您是否有更改,您可以在应用程序中执行类似操作。Identinterbackground:
,这样您就不会浪费时间。我确实会在我的应用程序上手动保存更改,因为我知道何时需要更改。我依靠上面的代码进行后台保存
来自苹果:
在调用save:方法之前,始终验证上下文是否有未提交的更改(使用hasChanges属性)。否则,核心数据可能会执行不必要的工作
为什么向谓词中甚至没有使用的项添加属性会起作用
我认为这更多地与获取请求有关,而不是持久存储。我相信这与你先前的说法有关。“relationship对象的存档属性设置为YES,但fetch仍会返回该属性。”我认为,一旦您解决了持久化问题,fetchRequest就不会返回该项。我没有读过这篇文章,也没有读过您的所有代码,但MOCs缓存数据,甚至fetch也不会迫使它再次进入数据库。因此,请收听NSManagedObjectContextDidSaveNotification通知并手动合并更改<强>。。主要是因为您需要告诉fetch results controller(!),因为它也有一个缓存您可以发布堆栈创建和fetch results controller创建代码吗?@MarcusS.Zarra done,为
TTPersistenceManager
和nsfetchedresultscocontroller
添加了源代码,您是否看到NSFetchedResultsController
fire的委托方法?您针对的是哪个版本的iOS?仅供参考,将notArchived放在复合谓词的第一位。您将获得更好的性能。您是否在每次刷卡后调用-performFetch:
@implementation TTPersistenceManager
- (id)initWithPersistenceType:(TTPersistenceType)persistenceType {
self = [super init];
if (self) {
_persistenceType = persistenceType;
}
return self;
}
- (void)initializeCoreData {
FCYAssert(!self.managedObjectModel, @"CoreData has already been initialized");
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
self.managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:@[bundle]];
self.persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel];
NSError *error = nil;
NSPersistentStore *store = [self.persistentStoreCoordinator addPersistentStoreWithType:[self storageType] configuration:nil URL:nil options:nil error:&error];
FCYAssert(store != nil, @"Failed create persistent store: %@\n%@", [error localizedDescription], [error userInfo]);
self.persistentStoreManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
self.persistentStoreManagedObjectContext.persistentStoreCoordinator = self.persistentStoreCoordinator;
self.persistentStoreManagedObjectContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
self.managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
self.managedObjectContext.parentContext = self.persistentStoreManagedObjectContext;
self.managedObjectContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy;
}
- (void)persist {
if (![self saveContext:self.managedObjectContext]) return;
[self saveContext:self.persistentStoreManagedObjectContext];
}
- (void)save {
[self saveContext:self.managedObjectContext];
}
#pragma mark - Private
- (NSString *)storageType {
if (self.persistenceType == TTPersistenceTypeSQLite) return NSSQLiteStoreType;
return NSInMemoryStoreType;
}
- (BOOL)saveContext:(NSManagedObjectContext *)context {
NSError *error = nil;
BOOL didSave = [self saveContext:context error:&error];
if (!didSave) {
TTLogError(@"Error saving context: %@\n\nUser Info:\n%@\n\nCall Stack:\n%@", error.localizedDescription, error.userInfo, [NSThread callStackSymbols]);
}
return didSave;
}
- (BOOL)saveContext:(NSManagedObjectContext *)context error:(NSError **)errorPtr {
__block BOOL hasChanges = NO;
[context performBlockAndWait:^{
hasChanges = [context hasChanges];
}];
if (!hasChanges) return YES;
__block NSError *error = nil;
__block BOOL didSave = NO;
[context performBlockAndWait:^{
didSave = [context save:&error];
}];
if (!didSave && error && errorPtr) {
*errorPtr = error;
}
return didSave;
}
@end