Context
I have a simple Core Data: stack MainQueueMOC -> PrivateBackgroundMOC -> PersistentStoreCoordinator, which is controlled by mine TTPersistenceManager, which looks like this:
typedef NS_ENUM(NSInteger, TTPersistenceType) {
TTPersistenceTypeInMemory,
TTPersistenceTypeSQLite
};
@interface TTPersistenceManager : NSObject
@property (strong, nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
- (id)initWithPersistenceType:(TTPersistenceType)persistenceType;
- (void)initializeCoreData;
- (void)save;
- (void)persist;
Currently, we only use memory in memory.
This is inspired by this article from Marcus Zarra . Thus, MainQueueMOC is the only source of truth, and PrivateBackgroundMOC is used only for storage in the background, and it is never published publicly. If you read the article, you will notice that I added a method called persist, the difference between saveand persist:
savesaves MainQueueMOC with performBlockAndWaitpersistsaves MainQueueMOC with performBlockAndWaitand PrivateBackgroundMOC withperformBlock
- :
, , , . , , .
, .
, save - , , , persist , .
, .
a NSFetchedResultsController , :
item.kind = "relationship" AND item.relationship.archived == NO
, . , relationship.archived = @YES, [TTPersistenceManager save] NSFetchedResultsController, . .
.
- (void)applicationDidEnterBackground:(UIApplication *)application {
[self.persistenceController persist];
}
persist, , .
relationship.archived = @YES;
[self.persistenceManager save];
[self.fetchedResultsController performFetch:&error];
item = [[self.fetchedResultsController] fetchedObjects] firstObject];
NSLog(@"item is archived %d", item.relationship.archived);
relationship archived, YES, .
. , , , , , , sqlite.
\ 1. updatedAt item, , relationship:
relationship.item.updatedAt = [NSDate date];
relationship.archived = @YES;
[self.persistenceManager save];
\ 2. persist save:
relationship.archived = @YES;
[self.persistenceManager persist]
?
, MOC, ?
item, ?
:
@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;
}