diff --git a/Code/CoreData/NSManagedObject+ActiveRecord.h b/Code/CoreData/NSManagedObject+ActiveRecord.h index 093e89a949..0c902fe74d 100644 --- a/Code/CoreData/NSManagedObject+ActiveRecord.h +++ b/Code/CoreData/NSManagedObject+ActiveRecord.h @@ -15,9 +15,8 @@ */ @interface NSManagedObjectContext (ActiveRecord) -+ (NSManagedObjectContext *)defaultContext; -+ (void)setDefaultContext:(NSManagedObjectContext *)context; -+ (NSManagedObjectContext *)contextForCurrentThread; ++ (NSManagedObjectContext *)contextForMainThread; ++ (NSManagedObjectContext *)contextForBackgroundThread; @end @@ -27,91 +26,57 @@ */ @interface NSManagedObject (ActiveRecord) -/** - * The NSEntityDescription for the Subclass - * defaults to the subclass className, may be overridden - */ -+ (NSEntityDescription*)MR_entity; - -/** - * Returns an initialized NSFetchRequest for the entity, with no predicate - */ -+ (NSFetchRequest*)MR_fetchRequest; - /** * Fetches all objects from the persistent store identified by the fetchRequest */ -+ (NSArray*)objectsWithFetchRequest:(NSFetchRequest*)fetchRequest; ++ (NSArray*)objectsWithFetchRequest:(NSFetchRequest*)fetchRequest inContext:(NSManagedObjectContext*)context; /** * Retrieves the number of objects that would be retrieved by the fetchRequest, * if executed */ -+ (NSUInteger)countOfObjectsWithFetchRequest:(NSFetchRequest*)fetchRequest; - -/** - * Fetches all objects from the persistent store via a set of fetch requests and - * returns all results in a single array. - */ -+ (NSArray*)objectsWithFetchRequests:(NSArray*)fetchRequests; ++ (NSUInteger)countOfObjectsWithFetchRequest:(NSFetchRequest*)fetchRequest inContext:(NSManagedObjectContext*)context; /** * Fetches the first object identified by the fetch request. A limit of one will be * applied to the fetch request before dispatching. */ -+ (id)objectWithFetchRequest:(NSFetchRequest*)fetchRequest; ++ (id)objectWithFetchRequest:(NSFetchRequest*)fetchRequest inContext:(NSManagedObjectContext*)context; /** * Fetches all objects from the persistent store by constructing a fetch request and * applying the predicate supplied. A short-cut for doing filtered searches on the objects * of this class under management. */ -+ (NSArray*)objectsWithPredicate:(NSPredicate*)predicate; ++ (NSArray*)objectsWithPredicate:(NSPredicate*)predicate inContext:(NSManagedObjectContext*)context; /** * Fetches the first object matching a predicate from the persistent store. A fetch request * will be constructed for you and a fetch limit of 1 will be applied. */ -+ (id)objectWithPredicate:(NSPredicate*)predicate; ++ (id)objectWithPredicate:(NSPredicate*)predicate inContext:(NSManagedObjectContext*)context; /** * Fetches all managed objects of this class from the persistent store as an array */ -+ (NSArray*)allObjects; ++ (NSArray*)allObjectsInContext:(NSManagedObjectContext*)context; /** * Returns a count of all managed objects of this class in the persistent store. On * error, will populate the error argument */ -+ (NSUInteger)count:(NSError**)error; - -/** - * Returns a count of all managed objects of this class in the persistent store. Deprecated - * use the error form above - * - * @deprecated - */ -+ (NSUInteger)count DEPRECATED_ATTRIBUTE; ++ (NSUInteger)countInContext:(NSManagedObjectContext*)context error:(NSError**)error; /** * Creates a new managed object and inserts it into the managedObjectContext. */ -+ (id)object; ++ (id)objectInContext:(NSManagedObjectContext *)context; /** * Returns YES when an object has not been saved to the managed object context yet */ - (BOOL)isNew; -/** - Finds the instance of the receiver's entity with the given value for the primary key attribute - in the managed object context for the current thread. - - @param primaryKeyValue The value for the receiving entity's primary key attribute. - @return The object with the primary key attribute equal to the given value or nil. - */ -+ (id)findByPrimaryKey:(id)primaryKeyValue; - /** Finds the instance of the receiver's entity with the given value for the primary key attribute in the given managed object context. @@ -124,90 +89,53 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// -+ (NSManagedObjectContext*)currentContext; - + (void)handleErrors:(NSError *)error; -+ (NSArray *)executeFetchRequest:(NSFetchRequest *)request; + (NSArray *)executeFetchRequest:(NSFetchRequest *)request inContext:(NSManagedObjectContext *)context; -+ (NSFetchRequest *)createFetchRequest; + (NSFetchRequest *)createFetchRequestInContext:(NSManagedObjectContext *)context; -+ (NSEntityDescription *)entityDescription; + (NSEntityDescription *)entityDescriptionInContext:(NSManagedObjectContext *)context; -+ (NSArray *)propertiesNamed:(NSArray *)properties; -+ (id)createEntity; + (id)createInContext:(NSManagedObjectContext *)context; -- (BOOL)deleteEntity; - (BOOL)deleteInContext:(NSManagedObjectContext *)context; -+ (BOOL)truncateAll; + (BOOL)truncateAllInContext:(NSManagedObjectContext *)context; + (NSArray *)ascendingSortDescriptors:(id)attributesToSortBy, ...; + (NSArray *)descendingSortDescriptors:(id)attributesToSortyBy, ...; -+ (NSNumber *)numberOfEntities; + (NSNumber *)numberOfEntitiesWithContext:(NSManagedObjectContext *)context; -+ (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm; + (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context; -+ (BOOL) hasAtLeastOneEntity; + (BOOL) hasAtLeastOneEntityInContext:(NSManagedObjectContext *)context; -+ (NSFetchRequest *)requestAll; + (NSFetchRequest *)requestAllInContext:(NSManagedObjectContext *)context; -+ (NSFetchRequest *)requestAllWhere:(NSString *)property isEqualTo:(id)value; + (NSFetchRequest *)requestAllWhere:(NSString *)property isEqualTo:(id)value inContext:(NSManagedObjectContext *)context; -+ (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm; + (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context; -+ (NSFetchRequest *)requestFirstByAttribute:(NSString *)attribute withValue:(id)searchValue; + (NSFetchRequest *)requestFirstByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context; -+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending; + (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context; -+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm; + (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context; -+ (NSArray *)findAll; + (NSArray *)findAllInContext:(NSManagedObjectContext *)context; -+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending; + (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context; -+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm; + (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context; -+ (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm; + (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context; -+ (NSNumber *)maxValueFor:(NSString *)property; -+ (id) objectWithMinValueFor:(NSString *)property; -+ (id) objectWithMinValueFor:(NSString *)property inContext:(NSManagedObjectContext *)context; - -+ (id)findFirst; + (id)findFirstInContext:(NSManagedObjectContext *)context; -+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm; + (id)findFirstWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context; -+ (id)findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)property ascending:(BOOL)ascending; + (id)findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)property ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context; -+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes; + (id)findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes inContext:(NSManagedObjectContext *)context; -+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending andRetrieveAttributes:(id)attributes, ...; + (id)findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context andRetrieveAttributes:(id)attributes, ...; -+ (id)findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue; + (id)findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context; -+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue; + (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context; -+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue andOrderBy:(NSString *)sortTerm ascending:(BOOL)ascending; + (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue andOrderBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context; #if TARGET_OS_IPHONE -+ (NSFetchedResultsController *)fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath; + (NSFetchedResultsController *)fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath inContext:(NSManagedObjectContext *)context; -+ (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group; + (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group inContext:(NSManagedObjectContext *)context; -+ (NSFetchedResultsController *)fetchRequestAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending; + (NSFetchedResultsController *)fetchRequestAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context; #endif diff --git a/Code/CoreData/NSManagedObject+ActiveRecord.m b/Code/CoreData/NSManagedObject+ActiveRecord.m index 6382ad5231..5500bb9493 100644 --- a/Code/CoreData/NSManagedObject+ActiveRecord.m +++ b/Code/CoreData/NSManagedObject+ActiveRecord.m @@ -22,25 +22,18 @@ static NSUInteger const kActiveRecordDefaultBatchSize = 10; static NSNumber *defaultBatchSize = nil; -static NSManagedObjectContext *defaultContext = nil; - RK_FIX_CATEGORY_BUG(NSManagedObjectContext_ActiveRecord) @implementation NSManagedObjectContext (ActiveRecord) -+ (NSManagedObjectContext *)defaultContext { - return defaultContext; -} - -+ (void)setDefaultContext:(NSManagedObjectContext *)newDefaultContext { - [newDefaultContext retain]; - [defaultContext release]; - defaultContext = newDefaultContext; ++ (NSManagedObjectContext *)contextForMainThread { + NSAssert([RKManagedObjectStore defaultObjectStore], @"[RKManagedObjectStore defaultObjectStore] cannot be nil"); + return [[RKManagedObjectStore defaultObjectStore] primaryManagedObjectContext]; } -+ (NSManagedObjectContext *)contextForCurrentThread { ++ (NSManagedObjectContext *)contextForBackgroundThread { NSAssert([RKManagedObjectStore defaultObjectStore], @"[RKManagedObjectStore defaultObjectStore] cannot be nil"); - return [[RKManagedObjectStore defaultObjectStore] managedObjectContextForCurrentThread]; + return [[RKManagedObjectStore defaultObjectStore] backgroundManagedObjectContext]; } @end @@ -51,49 +44,27 @@ @implementation NSManagedObject (ActiveRecord) #pragma mark - RKManagedObject methods -+ (NSEntityDescription*)MR_entity { - NSString* className = [NSString stringWithCString:class_getName([self class]) encoding:NSASCIIStringEncoding]; - return [NSEntityDescription entityForName:className inManagedObjectContext:[NSManagedObjectContext contextForCurrentThread]]; -} - -+ (NSFetchRequest*)MR_fetchRequest { - NSFetchRequest *fetchRequest = [[[NSFetchRequest alloc] init] autorelease]; - NSEntityDescription *entity = [self MR_entity]; - [fetchRequest setEntity:entity]; - return fetchRequest; -} - -+ (NSArray*)objectsWithFetchRequest:(NSFetchRequest*)fetchRequest { ++ (NSArray*)objectsWithFetchRequest:(NSFetchRequest*)fetchRequest inContext:(NSManagedObjectContext*)context { NSError* error = nil; - NSArray* objects = [[NSManagedObjectContext contextForCurrentThread] executeFetchRequest:fetchRequest error:&error]; + NSArray* objects = [context executeFetchRequest:fetchRequest error:&error]; if (objects == nil) { RKLogError(@"Error: %@", [error localizedDescription]); } return objects; } -+ (NSUInteger)countOfObjectsWithFetchRequest:(NSFetchRequest*)fetchRequest { ++ (NSUInteger)countOfObjectsWithFetchRequest:(NSFetchRequest*)fetchRequest inContext:(NSManagedObjectContext*)context { NSError* error = nil; - NSUInteger objectCount = [[NSManagedObjectContext contextForCurrentThread] countForFetchRequest:fetchRequest error:&error]; + NSUInteger objectCount = [context countForFetchRequest:fetchRequest error:&error]; if (objectCount == NSNotFound) { RKLogError(@"Error: %@", [error localizedDescription]); } return objectCount; } -+ (NSArray*)objectsWithFetchRequests:(NSArray*)fetchRequests { - NSMutableArray* mutableObjectArray = [[NSMutableArray alloc] init]; - for (NSFetchRequest* fetchRequest in fetchRequests) { - [mutableObjectArray addObjectsFromArray:[self objectsWithFetchRequest:fetchRequest]]; - } - NSArray* objects = [NSArray arrayWithArray:mutableObjectArray]; - [mutableObjectArray release]; - return objects; -} - -+ (id)objectWithFetchRequest:(NSFetchRequest*)fetchRequest { ++ (id)objectWithFetchRequest:(NSFetchRequest*)fetchRequest inContext:(NSManagedObjectContext*)context { [fetchRequest setFetchLimit:1]; - NSArray* objects = [self objectsWithFetchRequest:fetchRequest]; + NSArray* objects = [self objectsWithFetchRequest:fetchRequest inContext:context]; if ([objects count] == 0) { return nil; } else { @@ -101,34 +72,29 @@ + (id)objectWithFetchRequest:(NSFetchRequest*)fetchRequest { } } -+ (NSArray*)objectsWithPredicate:(NSPredicate*)predicate { - NSFetchRequest* fetchRequest = [self MR_fetchRequest]; ++ (NSArray*)objectsWithPredicate:(NSPredicate*)predicate inContext:(NSManagedObjectContext*)context { + NSFetchRequest* fetchRequest = [self fetchRequest]; [fetchRequest setPredicate:predicate]; - return [self objectsWithFetchRequest:fetchRequest]; + return [self objectsWithFetchRequest:fetchRequest inContext:context]; } -+ (id)objectWithPredicate:(NSPredicate*)predicate { - NSFetchRequest* fetchRequest = [self MR_fetchRequest]; ++ (id)objectWithPredicate:(NSPredicate*)predicate inContext:(NSManagedObjectContext*)context { + NSFetchRequest* fetchRequest = [self fetchRequest]; [fetchRequest setPredicate:predicate]; - return [self objectWithFetchRequest:fetchRequest]; + return [self objectWithFetchRequest:fetchRequest inContext:context]; } -+ (NSArray*)allObjects { - return [self objectsWithPredicate:nil]; ++ (NSArray*)allObjectsInContext:(NSManagedObjectContext*)context { + return [self objectsWithPredicate:nil inContext:context]; } -+ (NSUInteger)count:(NSError**)error { - NSFetchRequest* fetchRequest = [self MR_fetchRequest]; - return [[NSManagedObjectContext contextForCurrentThread] countForFetchRequest:fetchRequest error:error]; -} - -+ (NSUInteger)count { - NSError *error = nil; - return [self count:&error]; ++ (NSUInteger)countInContext:(NSManagedObjectContext*)context error:(NSError**)error { + NSFetchRequest* fetchRequest = [self fetchRequest]; + return [context countForFetchRequest:fetchRequest error:error]; } -+ (id)object { - id object = [[self alloc] initWithEntity:[self MR_entity] insertIntoManagedObjectContext:[NSManagedObjectContext contextForCurrentThread]]; ++ (id)objectInContext:(NSManagedObjectContext *)context { + id object = [[self alloc] initWithEntity:[self entity] insertIntoManagedObjectContext:context]; return [object autorelease]; } @@ -147,16 +113,8 @@ + (id)findByPrimaryKey:(id)primaryKeyValue inContext:(NSManagedObjectContext *)c return [self findFirstWithPredicate:predicate inContext:context]; } -+ (id)findByPrimaryKey:(id)primaryKeyValue { - return [self findByPrimaryKey:primaryKeyValue inContext:[NSManagedObjectContext contextForCurrentThread]]; -} - #pragma mark - MagicalRecord Ported Methods -+ (NSManagedObjectContext*)currentContext; { - return [NSManagedObjectContext contextForCurrentThread]; -} - + (void)setDefaultBatchSize:(NSUInteger)newBatchSize { @synchronized(defaultBatchSize) @@ -214,11 +172,6 @@ + (NSArray *)executeFetchRequest:(NSFetchRequest *)request inContext:(NSManagedO return results; } -+ (NSArray *)executeFetchRequest:(NSFetchRequest *)request -{ - return [self executeFetchRequest:request inContext:[self currentContext]]; -} - + (id)executeFetchRequestAndReturnFirstObject:(NSFetchRequest *)request inContext:(NSManagedObjectContext *)context { [request setFetchLimit:1]; @@ -231,11 +184,6 @@ + (id)executeFetchRequestAndReturnFirstObject:(NSFetchRequest *)request inContex return [results objectAtIndex:0]; } -+ (id)executeFetchRequestAndReturnFirstObject:(NSFetchRequest *)request -{ - return [self executeFetchRequestAndReturnFirstObject:request inContext:[self currentContext]]; -} - #if TARGET_OS_IPHONE + (void)performFetch:(NSFetchedResultsController *)controller { @@ -253,36 +201,6 @@ + (NSEntityDescription *)entityDescriptionInContext:(NSManagedObjectContext *)co return [NSEntityDescription entityForName:entityName inManagedObjectContext:context]; } -+ (NSEntityDescription *)entityDescription -{ - return [self entityDescriptionInContext:[self currentContext]]; -} - -+ (NSArray *)propertiesNamed:(NSArray *)properties -{ - NSEntityDescription *description = [self entityDescription]; - NSMutableArray *propertiesWanted = [NSMutableArray array]; - - if (properties) - { - NSDictionary *propDict = [description propertiesByName]; - - for (NSString *propertyName in properties) - { - NSPropertyDescription *property = [propDict objectForKey:propertyName]; - if (property) - { - [propertiesWanted addObject:property]; - } - else - { - RKLogError(@"Property '%@' not found in %@ properties for %@", propertyName, [propDict count], NSStringFromClass(self)); - } - } - } - return propertiesWanted; -} - + (NSArray *)sortAscending:(BOOL)ascending attributes:(id)attributesToSortBy, ... { NSMutableArray *attributes = [NSMutableArray array]; @@ -330,11 +248,6 @@ + (NSFetchRequest *)createFetchRequestInContext:(NSManagedObjectContext *)contex return request; } -+ (NSFetchRequest *)createFetchRequest -{ - return [self createFetchRequestInContext:[self currentContext]]; -} - #pragma mark - #pragma mark Number of Entities @@ -347,11 +260,6 @@ + (NSNumber *)numberOfEntitiesWithContext:(NSManagedObjectContext *)context return [NSNumber numberWithUnsignedInteger:count]; } -+ (NSNumber *)numberOfEntities -{ - return [self numberOfEntitiesWithContext:[self currentContext]]; -} - + (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context { NSError *error = nil; @@ -364,28 +272,13 @@ + (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm inContext: return [NSNumber numberWithUnsignedInteger:count]; } -+ (NSNumber *)numberOfEntitiesWithPredicate:(NSPredicate *)searchTerm; -{ - return [self numberOfEntitiesWithPredicate:searchTerm - inContext:[self currentContext]]; -} - + (BOOL)hasAtLeastOneEntityInContext:(NSManagedObjectContext *)context { return [[self numberOfEntitiesWithContext:context] intValue] > 0; } -+ (BOOL)hasAtLeastOneEntity -{ - return [self hasAtLeastOneEntityInContext:[self currentContext]]; -} - #pragma mark - #pragma mark Reqest Helpers -+ (NSFetchRequest *)requestAll -{ - return [self createFetchRequestInContext:[self currentContext]]; -} + (NSFetchRequest *)requestAllInContext:(NSManagedObjectContext *)context { @@ -400,11 +293,6 @@ + (NSFetchRequest *)requestAllWhere:(NSString *)property isEqualTo:(id)value inC return request; } -+ (NSFetchRequest *)requestAllWhere:(NSString *)property isEqualTo:(id)value -{ - return [self requestAllWhere:property isEqualTo:value inContext:[self currentContext]]; -} - + (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self createFetchRequestInContext:context]; @@ -414,11 +302,6 @@ + (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm inContex return request; } -+ (NSFetchRequest *)requestFirstWithPredicate:(NSPredicate *)searchTerm -{ - return [self requestFirstWithPredicate:searchTerm inContext:[self currentContext]]; -} - + (NSFetchRequest *)requestFirstByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context; { NSFetchRequest *request = [self createFetchRequestInContext:context]; @@ -427,11 +310,6 @@ + (NSFetchRequest *)requestFirstByAttribute:(NSString *)attribute withValue:(id) return request; } -+ (NSFetchRequest *)requestFirstByAttribute:(NSString *)attribute withValue:(id)searchValue; -{ - return [self requestFirstByAttribute:attribute withValue:searchValue inContext:[self currentContext]]; -} - + (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self requestAllInContext:context]; @@ -443,13 +321,6 @@ + (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)asce return request; } -+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending -{ - return [self requestAllSortedBy:sortTerm - ascending:ascending - inContext:[self currentContext]]; -} - + (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self requestAllInContext:context]; @@ -466,16 +337,6 @@ + (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)asce return request; } -+ (NSFetchRequest *)requestAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm; -{ - NSFetchRequest *request = [self requestAllSortedBy:sortTerm - ascending:ascending - withPredicate:searchTerm - inContext:[self currentContext]]; - return request; -} - - #pragma mark Finding Data #pragma mark - @@ -484,11 +345,6 @@ + (NSArray *)findAllInContext:(NSManagedObjectContext *)context return [self executeFetchRequest:[self requestAllInContext:context] inContext:context]; } -+ (NSArray *)findAll -{ - return [self findAllInContext:[self currentContext]]; -} - + (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self requestAllSortedBy:sortTerm ascending:ascending inContext:context]; @@ -496,13 +352,6 @@ + (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending inCo return [self executeFetchRequest:request inContext:context]; } -+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending -{ - return [self findAllSortedBy:sortTerm - ascending:ascending - inContext:[self currentContext]]; -} - + (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self requestAllSortedBy:sortTerm @@ -513,14 +362,6 @@ + (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending with return [self executeFetchRequest:request inContext:context]; } -+ (NSArray *)findAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm -{ - return [self findAllSortedBy:sortTerm - ascending:ascending - withPredicate:searchTerm - inContext:[self currentContext]]; -} - #pragma mark - #pragma mark NSFetchedResultsController helpers @@ -545,15 +386,6 @@ + (NSFetchedResultsController *)fetchRequestAllGroupedBy:(NSString *)group withP return [controller autorelease]; } -+ (NSFetchedResultsController *)fetchRequestAllGroupedBy:(NSString *)group withPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortTerm ascending:(BOOL)ascending -{ - return [self fetchRequestAllGroupedBy:group - withPredicate:searchTerm - sortedBy:sortTerm - ascending:ascending - inContext:[self currentContext]]; -} - + (NSFetchedResultsController *)fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath inContext:(NSManagedObjectContext *)context { NSFetchedResultsController *controller = [self fetchRequestAllGroupedBy:groupingKeyPath @@ -566,15 +398,6 @@ + (NSFetchedResultsController *)fetchAllSortedBy:(NSString *)sortTerm ascending: return controller; } -+ (NSFetchedResultsController *)fetchAllSortedBy:(NSString *)sortTerm ascending:(BOOL)ascending withPredicate:(NSPredicate *)searchTerm groupBy:(NSString *)groupingKeyPath -{ - return [self fetchAllSortedBy:sortTerm - ascending:ascending - withPredicate:searchTerm - groupBy:groupingKeyPath - inContext:[self currentContext]]; -} - + (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group inContext:(NSManagedObjectContext *)context { NSString *cacheName = nil; @@ -590,12 +413,6 @@ + (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy return [controller autorelease]; } -+ (NSFetchedResultsController *)fetchRequest:(NSFetchRequest *)request groupedBy:(NSString *)group -{ - return [self fetchRequest:request - groupedBy:group - inContext:[self currentContext]]; -} #endif #pragma mark - @@ -609,12 +426,6 @@ + (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm inContext:(NSManaged inContext:context]; } -+ (NSArray *)findAllWithPredicate:(NSPredicate *)searchTerm -{ - return [self findAllWithPredicate:searchTerm - inContext:[self currentContext]]; -} - + (id)findFirstInContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self createFetchRequestInContext:context]; @@ -622,11 +433,6 @@ + (id)findFirstInContext:(NSManagedObjectContext *)context return [self executeFetchRequestAndReturnFirstObject:request inContext:context]; } -+ (id)findFirst -{ - return [self findFirstInContext:[self currentContext]]; -} - + (id)findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self requestFirstByAttribute:attribute withValue:searchValue inContext:context]; @@ -634,25 +440,13 @@ + (id)findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue inCon return [self executeFetchRequestAndReturnFirstObject:request inContext:context]; } -+ (id)findFirstByAttribute:(NSString *)attribute withValue:(id)searchValue -{ - return [self findFirstByAttribute:attribute - withValue:searchValue - inContext:[self currentContext]]; -} - + (id)findFirstWithPredicate:(NSPredicate *)searchTerm inContext:(NSManagedObjectContext *)context { - NSFetchRequest *request = [self requestFirstWithPredicate:searchTerm]; + NSFetchRequest *request = [self requestFirstWithPredicate:searchTerm inContext:context]; return [self executeFetchRequestAndReturnFirstObject:request inContext:context]; } -+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm -{ - return [self findFirstWithPredicate:searchTerm inContext:[self currentContext]]; -} - + (id)findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)property ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self requestAllSortedBy:property ascending:ascending withPredicate:searchterm inContext:context]; @@ -660,14 +454,6 @@ + (id)findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)prop return [self executeFetchRequestAndReturnFirstObject:request inContext:context]; } -+ (id)findFirstWithPredicate:(NSPredicate *)searchterm sortedBy:(NSString *)property ascending:(BOOL)ascending -{ - return [self findFirstWithPredicate:searchterm - sortedBy:property - ascending:ascending - inContext:[self currentContext]]; -} - + (id)findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes inContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self createFetchRequestInContext:context]; @@ -676,14 +462,6 @@ + (id)findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NS return [self executeFetchRequestAndReturnFirstObject:request inContext:context]; } -+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm andRetrieveAttributes:(NSArray *)attributes -{ - return [self findFirstWithPredicate:searchTerm - andRetrieveAttributes:attributes - inContext:[self currentContext]]; -} - - + (id)findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context andRetrieveAttributes:(id)attributes, ... { NSFetchRequest *request = [self requestAllSortedBy:sortBy @@ -694,15 +472,6 @@ + (id)findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sort return [self executeFetchRequestAndReturnFirstObject:request inContext:context]; } -+ (id)findFirstWithPredicate:(NSPredicate *)searchTerm sortedBy:(NSString *)sortBy ascending:(BOOL)ascending andRetrieveAttributes:(id)attributes, ... -{ - return [self findFirstWithPredicate:searchTerm - sortedBy:sortBy - ascending:ascending - inContext:[self currentContext] - andRetrieveAttributes:attributes]; -} - + (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue inContext:(NSManagedObjectContext *)context { NSFetchRequest *request = [self createFetchRequestInContext:context]; @@ -712,28 +481,12 @@ + (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue inC return [self executeFetchRequest:request inContext:context]; } -+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue -{ - return [self findByAttribute:attribute - withValue:searchValue - inContext:[self currentContext]]; -} - + (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue andOrderBy:(NSString *)sortTerm ascending:(BOOL)ascending inContext:(NSManagedObjectContext *)context { NSPredicate *searchTerm = [NSPredicate predicateWithFormat:@"%K = %@", attribute, searchValue]; NSFetchRequest *request = [self requestAllSortedBy:sortTerm ascending:ascending withPredicate:searchTerm inContext:context]; - return [self executeFetchRequest:request]; -} - -+ (NSArray *)findByAttribute:(NSString *)attribute withValue:(id)searchValue andOrderBy:(NSString *)sortTerm ascending:(BOOL)ascending -{ - return [self findByAttribute:attribute - withValue:searchValue - andOrderBy:sortTerm - ascending:ascending - inContext:[self currentContext]]; + return [self executeFetchRequest:request inContext:context]; } + (id)createInContext:(NSManagedObjectContext *)context @@ -742,25 +495,12 @@ + (id)createInContext:(NSManagedObjectContext *)context return [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context]; } -+ (id)createEntity -{ - NSManagedObject *newEntity = [self createInContext:[self currentContext]]; - - return newEntity; -} - - (BOOL)deleteInContext:(NSManagedObjectContext *)context { [context deleteObject:self]; return YES; } -- (BOOL)deleteEntity -{ - [self deleteInContext:[[self class] currentContext]]; - return YES; -} - + (BOOL)truncateAllInContext:(NSManagedObjectContext *)context { NSArray *allEntities = [self findAllInContext:context]; @@ -771,33 +511,4 @@ + (BOOL)truncateAllInContext:(NSManagedObjectContext *)context return YES; } -+ (BOOL)truncateAll -{ - [self truncateAllInContext:[self currentContext]]; - return YES; -} - -+ (NSNumber *)maxValueFor:(NSString *)property -{ - NSManagedObject *obj = [[self class] findFirstByAttribute:property - withValue:[NSString stringWithFormat:@"max(%@)", property]]; - - return [obj valueForKey:property]; -} - -+ (id)objectWithMinValueFor:(NSString *)property inContext:(NSManagedObjectContext *)context -{ - NSFetchRequest *request = [[self class] createFetchRequestInContext:context]; - - NSPredicate *searchFor = [NSPredicate predicateWithFormat:@"SELF = %@ AND %K = min(%@)", self, property, property]; - [request setPredicate:searchFor]; - - return [[self class] executeFetchRequestAndReturnFirstObject:request inContext:context]; -} - -+ (id)objectWithMinValueFor:(NSString *)property -{ - return [[self class] objectWithMinValueFor:property inContext:[self currentContext]]; -} - @end diff --git a/Code/CoreData/RKEntityByAttributeCache.m b/Code/CoreData/RKEntityByAttributeCache.m index a227be26f8..f7d3ea7552 100644 --- a/Code/CoreData/RKEntityByAttributeCache.m +++ b/Code/CoreData/RKEntityByAttributeCache.m @@ -101,29 +101,31 @@ - (BOOL)shouldCoerceAttributeToString:(NSString *)attributeValue - (void)load { - RKLogDebug(@"Loading entity cache for Entity '%@' by attribute '%@'", self.entity.name, self.attribute); - NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; - [fetchRequest setEntity:self.entity]; - [fetchRequest setResultType:NSManagedObjectIDResultType]; - - NSError *error = nil; - NSArray *objectIDs = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error]; - [fetchRequest release]; - if (error) { - RKLogError(@"Failed to load entity cache: %@", error); - return; - } - - self.attributeValuesToObjectIDs = [NSMutableDictionary dictionaryWithCapacity:[objectIDs count]]; - for (NSManagedObjectID *objectID in objectIDs) { + [self.managedObjectContext performBlockAndWait:^{ + RKLogDebug(@"Loading entity cache for Entity '%@' by attribute '%@'", self.entity.name, self.attribute); + NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; + [fetchRequest setEntity:self.entity]; + [fetchRequest setResultType:NSManagedObjectIDResultType]; + NSError *error = nil; - NSManagedObject *object = [self.managedObjectContext existingObjectWithID:objectID error:&error]; - if (! object && error) { - RKLogError(@"Failed to retrieve managed object with ID %@: %@", objectID, error); + NSArray *objectIDs = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error]; + [fetchRequest release]; + if (error) { + RKLogError(@"Failed to load entity cache: %@", error); + return; } - - [self addObject:object]; - } + + self.attributeValuesToObjectIDs = [NSMutableDictionary dictionaryWithCapacity:[objectIDs count]]; + for (NSManagedObjectID *objectID in objectIDs) { + NSError *error = nil; + NSManagedObject *object = [self.managedObjectContext existingObjectWithID:objectID error:&error]; + if (! object && error) { + RKLogError(@"Failed to retrieve managed object with ID %@: %@", objectID, error); + } + + [self addObject:object]; + } + }]; } - (void)flush diff --git a/Code/CoreData/RKManagedObjectLoader.m b/Code/CoreData/RKManagedObjectLoader.m index bc65db0752..0f0fb40a15 100644 --- a/Code/CoreData/RKManagedObjectLoader.m +++ b/Code/CoreData/RKManagedObjectLoader.m @@ -88,7 +88,7 @@ - (void)objectMapper:(RKObjectMapper*)objectMapper didMapFromObject:(id)sourceOb // Overload the target object reader to return a thread-local copy of the target object - (id)targetObject { if ([NSThread isMainThread] == NO && _targetObjectID) { - return [self.objectStore objectWithID:_targetObjectID]; + return [[self.objectStore backgroundManagedObjectContext] objectWithID:_targetObjectID]; } return _targetObject; @@ -111,10 +111,13 @@ - (BOOL)prepareURLRequest { // into an error where the object context cannot be saved. We do this // right before send to avoid sequencing issues where the target object is // set before the managed object store. + NSManagedObjectContext* context = [(NSManagedObject*)self.targetObject managedObjectContext]; if (self.targetObject && [self.targetObject isKindOfClass:[NSManagedObject class]]) { - _deleteObjectOnFailure = [(NSManagedObject*)self.targetObject isNew]; - [self.objectStore save:nil]; - _targetObjectID = [[(NSManagedObject*)self.targetObject objectID] retain]; + [context performBlockAndWait:^{ + _deleteObjectOnFailure = [(NSManagedObject*)self.targetObject isNew]; + [self.objectStore saveContext:context withError:nil]; + _targetObjectID = [[(NSManagedObject*)self.targetObject objectID] retain]; + }]; } return [super prepareURLRequest]; @@ -141,7 +144,7 @@ - (void)deleteCachedObjectsMissingFromResult:(RKObjectMappingResult*)result { for (id object in cachedObjects) { if (NO == [results containsObject:object]) { RKLogTrace(@"Deleting orphaned object %@: not found in result set and expected at this resource path", object); - [[self.objectStore managedObjectContextForCurrentThread] deleteObject:object]; + [[self.objectStore backgroundManagedObjectContext] deleteObject:object]; } } } else { @@ -155,14 +158,14 @@ - (void)processMappingResult:(RKObjectMappingResult*)result { if (_targetObjectID && self.targetObject && self.method == RKRequestMethodDELETE) { NSManagedObject* backgroundThreadObject = [self.objectStore objectWithID:_targetObjectID]; RKLogInfo(@"Deleting local object %@ due to DELETE request", backgroundThreadObject); - [[self.objectStore managedObjectContextForCurrentThread] deleteObject:backgroundThreadObject]; + [[self.objectStore backgroundManagedObjectContext] deleteObject:backgroundThreadObject]; } // If the response was successful, save the store... if ([self.response isSuccessful]) { [self deleteCachedObjectsMissingFromResult:result]; NSError *error = nil; - BOOL success = [self.objectStore save:&error]; + BOOL success = [self.objectStore saveContext:[self.objectStore backgroundManagedObjectContext] withError:&error]; if (! success) { RKLogError(@"Failed to save managed object context after mapping completed: %@", [error localizedDescription]); NSMethodSignature* signature = [(NSObject *)self methodSignatureForSelector:@selector(informDelegateOfError:)]; @@ -170,11 +173,9 @@ - (void)processMappingResult:(RKObjectMappingResult*)result { [invocation setTarget:self]; [invocation setSelector:@selector(informDelegateOfError:)]; [invocation setArgument:&error atIndex:2]; - [invocation invokeOnMainThread]; + [invocation invoke]; - dispatch_async(dispatch_get_main_queue(), ^{ - [self finalizeLoad:success]; - }); + [self finalizeLoad:success]; return; } } @@ -187,7 +188,7 @@ - (void)processMappingResult:(RKObjectMappingResult*)result { [invocation setSelector:@selector(informDelegateOfObjectLoadWithResultDictionary:)]; [invocation setArgument:&dictionary atIndex:2]; [invocation setManagedObjectKeyPaths:_managedObjectKeyPaths forArgument:2]; - [invocation invokeOnMainThread]; + [invocation invoke]; } // Overloaded to handle deleting an object orphaned by a failed postObject: @@ -199,7 +200,7 @@ - (void)handleResponseError { RKLogInfo(@"Error response encountered: Deleting existing managed object with ID: %@", _targetObjectID); NSManagedObject* objectToDelete = [self.objectStore objectWithID:_targetObjectID]; if (objectToDelete) { - [[self.objectStore managedObjectContextForCurrentThread] deleteObject:objectToDelete]; + [[self.objectStore backgroundManagedObjectContext] deleteObject:objectToDelete]; [self.objectStore save:nil]; } else { RKLogWarning(@"Unable to delete existing managed object with ID: %@. Object not found in the store.", _targetObjectID); diff --git a/Code/CoreData/RKManagedObjectMapping.m b/Code/CoreData/RKManagedObjectMapping.m index 8008218f90..bf352c6954 100644 --- a/Code/CoreData/RKManagedObjectMapping.m +++ b/Code/CoreData/RKManagedObjectMapping.m @@ -171,7 +171,7 @@ - (id)mappableObjectForData:(id)mappableData { object = [self.objectStore.cacheStrategy findInstanceOfEntity:entity withPrimaryKeyAttribute:primaryKeyAttribute value:primaryKeyValue - inManagedObjectContext:[self.objectStore managedObjectContextForCurrentThread]]; + inManagedObjectContext:[self.objectStore backgroundManagedObjectContext]]; if (object && [self.objectStore.cacheStrategy respondsToSelector:@selector(didFetchObject:)]) { [self.objectStore.cacheStrategy didFetchObject:object]; @@ -180,7 +180,7 @@ - (id)mappableObjectForData:(id)mappableData { if (object == nil) { object = [[[NSManagedObject alloc] initWithEntity:entity - insertIntoManagedObjectContext:[_objectStore managedObjectContextForCurrentThread]] autorelease]; + insertIntoManagedObjectContext:[_objectStore backgroundManagedObjectContext]] autorelease]; if (primaryKeyAttribute && primaryKeyValue && ![primaryKeyValue isEqual:[NSNull null]]) { id coercedPrimaryKeyValue = [entity coerceValueForPrimaryKey:primaryKeyValue]; [object setValue:coercedPrimaryKeyValue forKey:primaryKeyAttribute]; diff --git a/Code/CoreData/RKManagedObjectMappingOperation.m b/Code/CoreData/RKManagedObjectMappingOperation.m index 4d52293e6b..fab26649f0 100644 --- a/Code/CoreData/RKManagedObjectMappingOperation.m +++ b/Code/CoreData/RKManagedObjectMappingOperation.m @@ -74,7 +74,7 @@ - (void)connectRelationship:(NSString *)relationshipName { relatedObject = [NSMutableSet set]; NSObject *cache = [[(RKManagedObjectMapping*)[self objectMapping] objectStore] cacheStrategy]; for (id foreignKey in valueOfLocalPrimaryKeyAttribute) { - id searchResult = [cache findInstanceOfEntity:objectMapping.entity withPrimaryKeyAttribute:primaryKeyAttributeOfRelatedObject value:foreignKey inManagedObjectContext:[[(RKManagedObjectMapping*)[self objectMapping] objectStore] managedObjectContextForCurrentThread]]; + id searchResult = [cache findInstanceOfEntity:objectMapping.entity withPrimaryKeyAttribute:primaryKeyAttributeOfRelatedObject value:foreignKey inManagedObjectContext:[[(RKManagedObjectMapping*)[self objectMapping] objectStore] backgroundManagedObjectContext]]; if (searchResult) { [relatedObject addObject:searchResult]; } @@ -106,30 +106,25 @@ - (void)connectRelationships { NSDictionary* relationshipsAndPrimaryKeyAttributes = [(RKManagedObjectMapping *)self.objectMapping relationshipsAndPrimaryKeyAttributes]; RKLogTrace(@"relationshipsAndPrimaryKeyAttributes: %@", relationshipsAndPrimaryKeyAttributes); for (NSString* relationshipName in relationshipsAndPrimaryKeyAttributes) { - if (self.queue) { - RKLogTrace(@"Enqueueing relationship connection using operation queue"); - __block RKManagedObjectMappingOperation *selfRef = self; - [self.queue addOperationWithBlock:^{ - [selfRef connectRelationship:relationshipName]; - }]; - } else { - [self connectRelationship:relationshipName]; - } + [self connectRelationship:relationshipName]; } } - (BOOL)performMapping:(NSError **)error { - BOOL success = [super performMapping:error]; - if ([self.objectMapping isKindOfClass:[RKManagedObjectMapping class]]) { - /** - NOTE: Processing the pending changes here ensures that the managed object context generates observable - callbacks that are important for maintaining any sort of cache that is consistent within a single - object mapping operation. As the MOC is only saved when the aggregate operation is processed, we must - manually invoke processPendingChanges to prevent recreating objects with the same primary key. - See https://github.com/RestKit/RestKit/issues/661 - */ - [self connectRelationships]; - } + __block BOOL success; + [[NSManagedObjectContext contextForBackgroundThread] performBlockAndWait:^{ + success = [super performMapping:error]; + if ([self.objectMapping isKindOfClass:[RKManagedObjectMapping class]]) { + /** + NOTE: Processing the pending changes here ensures that the managed object context generates observable + callbacks that are important for maintaining any sort of cache that is consistent within a single + object mapping operation. As the MOC is only saved when the aggregate operation is processed, we must + manually invoke processPendingChanges to prevent recreating objects with the same primary key. + See https://github.com/RestKit/RestKit/issues/661 + */ + [self connectRelationships]; + } + }]; return success; } diff --git a/Code/CoreData/RKManagedObjectSeeder.m b/Code/CoreData/RKManagedObjectSeeder.m index f779984f41..96d0cc5593 100644 --- a/Code/CoreData/RKManagedObjectSeeder.m +++ b/Code/CoreData/RKManagedObjectSeeder.m @@ -164,7 +164,8 @@ - (void)seedObjectsFromFile:(NSString *)fileName withObjectMapping:(RKObjectMapp - (void)finalizeSeedingAndExit { NSError *error = nil; - BOOL success = [[_manager objectStore] save:&error]; + RKManagedObjectStore* objectStore = [_manager objectStore]; + BOOL success = [objectStore saveContext:objectStore.primaryManagedObjectContext withError:&error]; if (! success) { RKLogError(@"[RestKit] RKManagedObjectSeeder: Error saving object context: %@", [error localizedDescription]); } diff --git a/Code/CoreData/RKManagedObjectStore.h b/Code/CoreData/RKManagedObjectStore.h index 75bfd8e64f..abb83cdf87 100644 --- a/Code/CoreData/RKManagedObjectStore.h +++ b/Code/CoreData/RKManagedObjectStore.h @@ -51,7 +51,7 @@ extern NSString* const RKManagedObjectStoreDidFailSaveNotification; NSString* _storeFilename; NSString* _pathToStoreFile; NSManagedObjectModel* _managedObjectModel; - NSPersistentStoreCoordinator* _persistentStoreCoordinator; + NSPersistentContainer* _persistentContainer; } // The delegate for this object store @@ -65,7 +65,7 @@ extern NSString* const RKManagedObjectStoreDidFailSaveNotification; // Core Data @property (nonatomic, readonly) NSManagedObjectModel* managedObjectModel; -@property (nonatomic, readonly) NSPersistentStoreCoordinator* persistentStoreCoordinator; +@property (nonatomic, readonly) NSPersistentContainer* persistentContainer; ///----------------------------------------------------------------------------- /// @name Accessing the Default Object Store @@ -133,25 +133,7 @@ extern NSString* const RKManagedObjectStoreDidFailSaveNotification; /** * Save the current contents of the managed object store */ -- (BOOL)save:(NSError **)error; - -/** - * This deletes and recreates the managed object context and - * persistent store, effectively clearing all data - */ -- (void)deletePersistentStoreUsingSeedDatabaseName:(NSString *)seedFile; -- (void)deletePersistentStore; - -/** - * Retrieves a model object from the appropriate context using the objectId - */ -- (NSManagedObject*)objectWithID:(NSManagedObjectID*)objectID; - -/** - * Retrieves a array of model objects from the appropriate context using - * an array of NSManagedObjectIDs - */ -- (NSArray*)objectsWithIDs:(NSArray*)objectIDs; +- (BOOL)saveContext:(NSManagedObjectContext*)context withError:(NSError **)error; ///----------------------------------------------------------------------------- /// @name Retrieving Managed Object Contexts @@ -162,17 +144,6 @@ extern NSString* const RKManagedObjectStoreDidFailSaveNotification; the object store was created. */ @property (nonatomic, retain, readonly) NSManagedObjectContext *primaryManagedObjectContext; - -/** - Instantiates a new managed object context - */ -- (NSManagedObjectContext *)newManagedObjectContext; - -/* - * This returns an appropriate managed object context for this object store. - * Because of the intrecacies of how Core Data works across threads it returns - * a different NSManagedObjectContext for each thread. - */ -- (NSManagedObjectContext *)managedObjectContextForCurrentThread; +@property (nonatomic, retain, readonly) NSManagedObjectContext *backgroundManagedObjectContext; @end diff --git a/Code/CoreData/RKManagedObjectStore.m b/Code/CoreData/RKManagedObjectStore.m index 3ec0f46210..92de0881bc 100644 --- a/Code/CoreData/RKManagedObjectStore.m +++ b/Code/CoreData/RKManagedObjectStore.m @@ -43,11 +43,11 @@ @interface RKManagedObjectStore () @property (nonatomic, retain, readwrite) NSManagedObjectContext *primaryManagedObjectContext; +@property (nonatomic, retain, readwrite) NSManagedObjectContext *backgroundManagedObjectContext; +@property (nonatomic, retain, readwrite) NSPersistentContainer *persistentContainer; - (id)initWithStoreFilename:(NSString *)storeFilename inDirectory:(NSString *)nilOrDirectoryPath usingSeedDatabaseName:(NSString *)nilOrNameOfSeedDatabaseInMainBundle managedObjectModel:(NSManagedObjectModel*)nilOrManagedObjectModel delegate:(id)delegate; -- (void)createPersistentStoreCoordinator; - (void)createStoreIfNecessaryUsingSeedDatabase:(NSString*)seedDatabase; -- (NSManagedObjectContext*)newManagedObjectContext; @end @implementation RKManagedObjectStore @@ -56,9 +56,10 @@ @implementation RKManagedObjectStore @synthesize storeFilename = _storeFilename; @synthesize pathToStoreFile = _pathToStoreFile; @synthesize managedObjectModel = _managedObjectModel; -@synthesize persistentStoreCoordinator = _persistentStoreCoordinator; +@synthesize persistentContainer = _persistentContainer; @synthesize cacheStrategy = _cacheStrategy; @synthesize primaryManagedObjectContext; +@synthesize backgroundManagedObjectContext; + (RKManagedObjectStore *)defaultObjectStore { return defaultObjectStore; @@ -68,8 +69,6 @@ + (void)setDefaultObjectStore:(RKManagedObjectStore *)objectStore { [objectStore retain]; [defaultObjectStore release]; defaultObjectStore = objectStore; - - [NSManagedObjectContext setDefaultContext:objectStore.primaryManagedObjectContext]; } + (void)deleteStoreAtPath:(NSString *)path @@ -124,19 +123,26 @@ - (id)initWithStoreFilename:(NSString *)storeFilename inDirectory:(NSString *)ni _pathToStoreFile = [[nilOrDirectoryPath stringByAppendingPathComponent:_storeFilename] retain]; if (nilOrManagedObjectModel == nil) { - // NOTE: allBundles permits Core Data setup in unit tests - nilOrManagedObjectModel = [NSManagedObjectModel mergedModelFromBundles:[NSBundle allBundles]]; + nilOrManagedObjectModel = [NSManagedObjectModel mergedModelFromBundles:@[[NSBundle mainBundle]]]; } - NSMutableArray* allManagedObjectModels = [NSMutableArray arrayWithObject:nilOrManagedObjectModel]; - _managedObjectModel = [[NSManagedObjectModel modelByMergingModels:allManagedObjectModels] retain]; + _managedObjectModel = [nilOrManagedObjectModel retain]; _delegate = delegate; if (nilOrNameOfSeedDatabaseInMainBundle) { [self createStoreIfNecessaryUsingSeedDatabase:nilOrNameOfSeedDatabaseInMainBundle]; } - [self createPersistentStoreCoordinator]; - self.primaryManagedObjectContext = [[self newManagedObjectContext] autorelease]; + [self createPersistentContainerWithName:storeFilename model:_managedObjectModel]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(mergeBackgroundContextChanges:) + name:NSManagedObjectContextDidSaveNotification + object:self.backgroundManagedObjectContext]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(mergeMainContextChanges:) + name:NSManagedObjectContextDidSaveNotification + object:self.primaryManagedObjectContext]; _cacheStrategy = [RKInMemoryManagedObjectCache new]; @@ -152,50 +158,8 @@ - (id)initWithStoreFilename:(NSString *)storeFilename inDirectory:(NSString *)ni return self; } -- (void)setThreadLocalObject:(id)value forKey:(id)key { - NSMutableDictionary* threadDictionary = [[NSThread currentThread] threadDictionary]; - NSString *objectStoreKey = [NSString stringWithFormat:@"RKManagedObjectStore_%p", self]; - if (! [threadDictionary valueForKey:objectStoreKey]) { - [threadDictionary setValue:[NSMutableDictionary dictionary] forKey:objectStoreKey]; - } - - [[threadDictionary objectForKey:objectStoreKey] setObject:value forKey:key]; -} - -- (id)threadLocalObjectForKey:(id)key { - NSMutableDictionary* threadDictionary = [[NSThread currentThread] threadDictionary]; - NSString *objectStoreKey = [NSString stringWithFormat:@"RKManagedObjectStore_%p", self]; - if (! [threadDictionary valueForKey:objectStoreKey]) { - [threadDictionary setObject:[NSMutableDictionary dictionary] forKey:objectStoreKey]; - } - - return [[threadDictionary objectForKey:objectStoreKey] objectForKey:key]; -} - -- (void)removeThreadLocalObjectForKey:(id)key { - NSMutableDictionary* threadDictionary = [[NSThread currentThread] threadDictionary]; - NSString *objectStoreKey = [NSString stringWithFormat:@"RKManagedObjectStore_%p", self]; - if (! [threadDictionary valueForKey:objectStoreKey]) { - [threadDictionary setObject:[NSMutableDictionary dictionary] forKey:objectStoreKey]; - } - - [[threadDictionary objectForKey:objectStoreKey] removeObjectForKey:key]; -} - -- (void)clearThreadLocalStorage { - // Clear out our Thread local information - NSManagedObjectContext *managedObjectContext = [self threadLocalObjectForKey:RKManagedObjectStoreThreadDictionaryContextKey]; - if (managedObjectContext) { - [self removeThreadLocalObjectForKey:RKManagedObjectStoreThreadDictionaryContextKey]; - } - if ([self threadLocalObjectForKey:RKManagedObjectStoreThreadDictionaryEntityCacheKey]) { - [self removeThreadLocalObjectForKey:RKManagedObjectStoreThreadDictionaryEntityCacheKey]; - } -} - - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; - [self clearThreadLocalStorage]; [_storeFilename release]; _storeFilename = nil; @@ -204,12 +168,14 @@ - (void)dealloc { [_managedObjectModel release]; _managedObjectModel = nil; - [_persistentStoreCoordinator release]; - _persistentStoreCoordinator = nil; + [_persistentContainer release]; + _persistentContainer = nil; [_cacheStrategy release]; _cacheStrategy = nil; [primaryManagedObjectContext release]; primaryManagedObjectContext = nil; + [backgroundManagedObjectContext release]; + backgroundManagedObjectContext = nil; [super dealloc]; } @@ -218,76 +184,70 @@ - (void)dealloc { Performs the save action for the application, which is to send the save: message to the application's managed object context. */ -- (BOOL)save:(NSError **)error { - NSManagedObjectContext* moc = [self managedObjectContextForCurrentThread]; - NSError *localError = nil; - - @try { - if (![moc save:&localError]) { - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(managedObjectStore:didFailToSaveContext:error:exception:)]) { - [self.delegate managedObjectStore:self didFailToSaveContext:moc error:localError exception:nil]; - } - - NSDictionary* userInfo = [NSDictionary dictionaryWithObject:localError forKey:@"error"]; - [[NSNotificationCenter defaultCenter] postNotificationName:RKManagedObjectStoreDidFailSaveNotification object:self userInfo:userInfo]; - - if ([[localError domain] isEqualToString:@"NSCocoaErrorDomain"]) { - NSDictionary *userInfo = [localError userInfo]; - NSArray *errors = [userInfo valueForKey:@"NSDetailedErrors"]; - if (errors) { - for (NSError *detailedError in errors) { - NSDictionary *subUserInfo = [detailedError userInfo]; - RKLogError(@"Core Data Save Error\n \ +- (BOOL)saveContext:(NSManagedObjectContext*)context withError:(NSError **)error { + __block NSError *localError = nil; + __block BOOL success = NO; + + [context performBlockAndWait:^{ + @try { + if (![context save:&localError]) { + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(managedObjectStore:didFailToSaveContext:error:exception:)]) { + [self.delegate managedObjectStore:self didFailToSaveContext:context error:localError exception:nil]; + } + + NSDictionary* userInfo = [NSDictionary dictionaryWithObject:localError forKey:@"error"]; + [[NSNotificationCenter defaultCenter] postNotificationName:RKManagedObjectStoreDidFailSaveNotification object:self userInfo:userInfo]; + + if ([[localError domain] isEqualToString:@"NSCocoaErrorDomain"]) { + NSDictionary *userInfo = [localError userInfo]; + NSArray *errors = [userInfo valueForKey:@"NSDetailedErrors"]; + if (errors) { + for (NSError *detailedError in errors) { + NSDictionary *subUserInfo = [detailedError userInfo]; + RKLogError(@"Core Data Save Error\n \ NSLocalizedDescription:\t\t%@\n \ NSValidationErrorKey:\t\t\t%@\n \ NSValidationErrorPredicate:\t%@\n \ NSValidationErrorObject:\n%@\n", - [subUserInfo valueForKey:@"NSLocalizedDescription"], - [subUserInfo valueForKey:@"NSValidationErrorKey"], - [subUserInfo valueForKey:@"NSValidationErrorPredicate"], - [subUserInfo valueForKey:@"NSValidationErrorObject"]); + [subUserInfo valueForKey:@"NSLocalizedDescription"], + [subUserInfo valueForKey:@"NSValidationErrorKey"], + [subUserInfo valueForKey:@"NSValidationErrorPredicate"], + [subUserInfo valueForKey:@"NSValidationErrorObject"]); + } } - } - else { - RKLogError(@"Core Data Save Error\n \ + else { + RKLogError(@"Core Data Save Error\n \ NSLocalizedDescription:\t\t%@\n \ NSValidationErrorKey:\t\t\t%@\n \ NSValidationErrorPredicate:\t%@\n \ NSValidationErrorObject:\n%@\n", - [userInfo valueForKey:@"NSLocalizedDescription"], - [userInfo valueForKey:@"NSValidationErrorKey"], - [userInfo valueForKey:@"NSValidationErrorPredicate"], - [userInfo valueForKey:@"NSValidationErrorObject"]); + [userInfo valueForKey:@"NSLocalizedDescription"], + [userInfo valueForKey:@"NSValidationErrorKey"], + [userInfo valueForKey:@"NSValidationErrorPredicate"], + [userInfo valueForKey:@"NSValidationErrorObject"]); + } } + + if (error) { + *error = localError; + } + + success = NO; } - - if (error) { - *error = localError; - } - - return NO; } - } - @catch (NSException* e) { - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(managedObjectStore:didFailToSaveContext:error:exception:)]) { - [self.delegate managedObjectStore:self didFailToSaveContext:moc error:nil exception:e]; - } - else { - @throw; + @catch (NSException* e) { + if (self.delegate != nil && [self.delegate respondsToSelector:@selector(managedObjectStore:didFailToSaveContext:error:exception:)]) { + [self.delegate managedObjectStore:self didFailToSaveContext:context error:nil exception:e]; + } + else { + @throw; + } } - } - - return YES; -} - -- (NSManagedObjectContext *)newManagedObjectContext { - NSManagedObjectContext *managedObjectContext = [[NSManagedObjectContext alloc] init]; - [managedObjectContext setPersistentStoreCoordinator:self.persistentStoreCoordinator]; - [managedObjectContext setUndoManager:nil]; - [managedObjectContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy]; - managedObjectContext.managedObjectStore = self; + + success = YES; + }]; - return managedObjectContext; + return success; } - (void)createStoreIfNecessaryUsingSeedDatabase:(NSString*)seedDatabase { @@ -308,115 +268,39 @@ - (void)createStoreIfNecessaryUsingSeedDatabase:(NSString*)seedDatabase { } } -- (void)createPersistentStoreCoordinator { - NSAssert(_managedObjectModel, @"Cannot create persistent store coordinator without a managed object model"); - NSAssert(!_persistentStoreCoordinator, @"Cannot create persistent store coordinator: one already exists."); - NSURL *storeURL = [NSURL fileURLWithPath:self.pathToStoreFile]; - - NSError *error; - _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:_managedObjectModel]; - - // Allow inferred migration from the original version of the application. - NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, - [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, - @{@"journal_mode" : @"DELETE"}, NSSQLitePragmasOption, nil]; - - if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) { - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(managedObjectStore:didFailToCreatePersistentStoreCoordinatorWithError:)]) { - [self.delegate managedObjectStore:self didFailToCreatePersistentStoreCoordinatorWithError:error]; - } else { - NSAssert(NO, @"Managed object store failed to create persistent store coordinator: %@", error); - } - } +- (void)createPersistentContainerWithName:(NSString*)name model:(NSManagedObjectModel*)model { + NSAssert(_managedObjectModel, @"Cannot create persistent container without a managed object model"); + NSAssert(!_persistentContainer, @"Cannot create persistent container: one already exists."); + + _persistentContainer = [[NSPersistentContainer alloc] initWithName:name managedObjectModel:self.managedObjectModel]; + + NSPersistentStoreDescription *storeDescription = self.persistentContainer.persistentStoreDescriptions.firstObject; + storeDescription.URL = [NSURL fileURLWithPath:self.pathToStoreFile]; + storeDescription.shouldMigrateStoreAutomatically = YES; + storeDescription.shouldInferMappingModelAutomatically = YES; + + [self.persistentContainer loadPersistentStoresWithCompletionHandler: + ^(NSPersistentStoreDescription *desc, NSError *error) { + NSAssert(!error, @"Failed to load store: %@", error); + self.persistentContainer.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy; + self.primaryManagedObjectContext = [self.persistentContainer viewContext]; + self.backgroundManagedObjectContext = [[self.persistentContainer newBackgroundContext] autorelease]; + self.backgroundManagedObjectContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy; + }]; } -- (void)deletePersistentStoreUsingSeedDatabaseName:(NSString *)seedFile { - NSURL* storeURL = [NSURL fileURLWithPath:self.pathToStoreFile]; - NSError* error = nil; - if ([[NSFileManager defaultManager] fileExistsAtPath:storeURL.path]) { - if (![[NSFileManager defaultManager] removeItemAtPath:storeURL.path error:&error]) { - if (self.delegate != nil && [self.delegate respondsToSelector:@selector(managedObjectStore:didFailToDeletePersistentStore:error:)]) { - [self.delegate managedObjectStore:self didFailToDeletePersistentStore:self.pathToStoreFile error:error]; - } - else { - NSAssert(NO, @"Managed object store failed to delete persistent store : %@", error); - } - } - } else { - RKLogWarning(@"Asked to delete persistent store but no store file exists at path: %@", storeURL.path); - } - - [_persistentStoreCoordinator release]; - _persistentStoreCoordinator = nil; - - if (seedFile) { - [self createStoreIfNecessaryUsingSeedDatabase:seedFile]; - } - - [self createPersistentStoreCoordinator]; - - // Recreate the MOC - self.primaryManagedObjectContext = [[self newManagedObjectContext] autorelease]; -} - -- (void)deletePersistentStore { - [self deletePersistentStoreUsingSeedDatabaseName:nil]; -} - -- (NSManagedObjectContext *)managedObjectContextForCurrentThread { - if ([NSThread isMainThread]) { - return self.primaryManagedObjectContext; - } - - // Background threads leverage thread-local storage - NSManagedObjectContext* managedObjectContext = [self threadLocalObjectForKey:RKManagedObjectStoreThreadDictionaryContextKey]; - if (!managedObjectContext) { - managedObjectContext = [self newManagedObjectContext]; - - // Store into thread local storage dictionary - [self setThreadLocalObject:managedObjectContext forKey:RKManagedObjectStoreThreadDictionaryContextKey]; - [managedObjectContext release]; - - // If we are a background Thread MOC, we need to inform the main thread on save - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(mergeChanges:) - name:NSManagedObjectContextDidSaveNotification - object:managedObjectContext]; - } - - return managedObjectContext; -} - -- (void)mergeChangesOnMainThreadWithNotification:(NSNotification*)notification { - assert([NSThread isMainThread]); - [self.primaryManagedObjectContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) - withObject:notification - waitUntilDone:YES]; -} - -- (void)mergeChanges:(NSNotification *)notification { +- (void)mergeBackgroundContextChanges:(NSNotification *)notification { // Merge changes into the main context on the main thread - [self performSelectorOnMainThread:@selector(mergeChangesOnMainThreadWithNotification:) withObject:notification waitUntilDone:YES]; + [self.primaryManagedObjectContext performBlock:^{ + [self.primaryManagedObjectContext mergeChangesFromContextDidSaveNotification:notification]; + }]; } -#pragma mark - -#pragma mark Helpers - -- (NSManagedObject*)objectWithID:(NSManagedObjectID *)objectID { - NSAssert(objectID, @"Cannot fetch a managedObject with a nil objectID"); - return [[self managedObjectContextForCurrentThread] objectWithID:objectID]; -} - -- (NSArray*)objectsWithIDs:(NSArray*)objectIDs { - NSMutableArray* objects = [[NSMutableArray alloc] init]; - for (NSManagedObjectID* objectID in objectIDs) { - [objects addObject:[self objectWithID:objectID]]; - } - NSArray* objectArray = [NSArray arrayWithArray:objects]; - [objects release]; - - return objectArray; +- (void)mergeMainContextChanges:(NSNotification *)notification { + // Merge changes into the background context on the context's thread + [self.backgroundManagedObjectContext performBlock:^{ + [self.backgroundManagedObjectContext mergeChangesFromContextDidSaveNotification:notification]; + }]; } @end diff --git a/Code/CoreData/RKManagedObjectThreadSafeInvocation.m b/Code/CoreData/RKManagedObjectThreadSafeInvocation.m index d4d805c5e1..254610d181 100644 --- a/Code/CoreData/RKManagedObjectThreadSafeInvocation.m +++ b/Code/CoreData/RKManagedObjectThreadSafeInvocation.m @@ -76,7 +76,7 @@ - (void)deserializeManagedObjectIDsForArgument:(id)argument withKeyPaths:(NSSet* id value = [argument valueForKeyPath:keyPath]; if ([value isKindOfClass:[NSManagedObjectID class]]) { NSAssert(self.objectStore, @"Object store cannot be nil"); - NSManagedObject* managedObject = [self.objectStore objectWithID:(NSManagedObjectID*)value]; + NSManagedObject* managedObject = [[self.objectStore primaryManagedObjectContext] objectWithID:(NSManagedObjectID*)value]; NSAssert(managedObject, @"Expected managed object for ID %@, got nil", value); [self setValue:managedObject forKeyPathOrKey:keyPath object:argument]; } else if ([value respondsToSelector:@selector(allObjects)]) { @@ -84,7 +84,7 @@ - (void)deserializeManagedObjectIDsForArgument:(id)argument withKeyPaths:(NSSet* for (id subObject in value) { if ([subObject isKindOfClass:[NSManagedObjectID class]]) { NSAssert(self.objectStore, @"Object store cannot be nil"); - NSManagedObject* managedObject = [self.objectStore objectWithID:(NSManagedObjectID*)subObject]; + NSManagedObject* managedObject = [[self.objectStore primaryManagedObjectContext] objectWithID:(NSManagedObjectID*)subObject]; [collection addObject:managedObject]; } else { [collection addObject:subObject]; diff --git a/Code/Network/RKRequestQueue.m b/Code/Network/RKRequestQueue.m index 88eb637360..a1cf1dc296 100644 --- a/Code/Network/RKRequestQueue.m +++ b/Code/Network/RKRequestQueue.m @@ -178,6 +178,8 @@ - (void)dealloc { _loadingRequests = nil; [_requests release]; _requests = nil; + [_name release]; + _name = nil; [super dealloc]; } diff --git a/Code/ObjectMapping/RKObjectLoader.h b/Code/ObjectMapping/RKObjectLoader.h index e80453f2b1..d52f1958fc 100644 --- a/Code/ObjectMapping/RKObjectLoader.h +++ b/Code/ObjectMapping/RKObjectLoader.h @@ -138,7 +138,6 @@ typedef void(^RKObjectLoaderDidLoadObjectsDictionaryBlock)(NSDictionary *diction @interface RKObjectLoader : RKRequest { id _sourceObject; id _targetObject; - dispatch_queue_t _mappingQueue; } /** @@ -251,13 +250,6 @@ typedef void(^RKObjectLoaderDidLoadObjectsDictionaryBlock)(NSDictionary *diction */ @property (nonatomic, retain) NSObject *targetObject; -/** - The Grand Central Dispatch queue to perform our parsing and object mapping - within. By default, object loaders will use the mappingQueue from the RKObjectManager - that created the loader. You can override this on a per-loader basis as necessary. - */ -@property (nonatomic, assign) dispatch_queue_t mappingQueue; - /////////////////////////////////////////////////////////////////////////////////////////// /** diff --git a/Code/ObjectMapping/RKObjectLoader.m b/Code/ObjectMapping/RKObjectLoader.m index eed310762f..a7ba5e3cfc 100644 --- a/Code/ObjectMapping/RKObjectLoader.m +++ b/Code/ObjectMapping/RKObjectLoader.m @@ -27,6 +27,7 @@ #import "RKRequest_Internals.h" #import "RKObjectMappingProvider+Contexts.h" #import "RKObjectSerializer.h" +#import "NSManagedObject+ActiveRecord.h" // Set Logging Component #undef RKLogComponent @@ -52,7 +53,6 @@ @implementation RKObjectLoader @synthesize serializationMapping = _serializationMapping; @synthesize serializationMIMEType = _serializationMIMEType; @synthesize sourceObject = _sourceObject; -@synthesize mappingQueue = _mappingQueue; @synthesize onDidFailWithError = _onDidFailWithError; @synthesize onDidLoadObject = _onDidLoadObject; @synthesize onDidLoadObjects = _onDidLoadObjects; @@ -69,7 +69,6 @@ - (id)initWithURL:(RKURL *)URL mappingProvider:(RKObjectMappingProvider *)mappin self = [super initWithURL:URL]; if (self) { _mappingProvider = [mappingProvider retain]; - _mappingQueue = [RKObjectManager defaultMappingQueue]; } return self; @@ -109,11 +108,13 @@ - (void)reset { } - (void)informDelegateOfError:(NSError *)error { - [(NSObject*)_delegate objectLoader:self didFailWithError:error]; + dispatch_async(dispatch_get_main_queue(), ^{ + [(NSObject*)_delegate objectLoader:self didFailWithError:error]; - if (self.onDidFailWithError) { - self.onDidFailWithError(error); - } + if (self.onDidFailWithError) { + self.onDidFailWithError(error); + } + }); } #pragma mark - Response Processing @@ -125,17 +126,13 @@ - (void)finalizeLoad:(BOOL)successful { self.loaded = successful; if ([self.delegate respondsToSelector:@selector(objectLoaderDidFinishLoading:)]) { - [(NSObject*)self.delegate performSelectorOnMainThread:@selector(objectLoaderDidFinishLoading:) - withObject:self waitUntilDone:YES]; + [self.delegate objectLoaderDidFinishLoading:self]; } - [[NSNotificationCenter defaultCenter] postNotificationName:RKRequestDidFinishLoadingNotification object:self]; } // Invoked on the main thread. Inform the delegate. - (void)informDelegateOfObjectLoadWithResultDictionary:(NSDictionary*)resultDictionary { - NSAssert([NSThread isMainThread], @"RKObjectLoaderDelegate callbacks must occur on the main thread"); - RKObjectMappingResult* result = [RKObjectMappingResult mappingResultWithDictionary:resultDictionary]; // Dictionary callback @@ -261,11 +258,11 @@ - (RKObjectMappingResult*)performMapping:(NSError**)error { } - (void)performMappingInDispatchQueue { - NSAssert(self.mappingQueue, @"mappingQueue cannot be nil"); - dispatch_async(self.mappingQueue, ^{ + NSManagedObjectContext* context = [NSManagedObjectContext contextForBackgroundThread]; + [context performBlock:^{ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; - RKLogDebug(@"Beginning object mapping activities within GCD queue labeled: %s", dispatch_queue_get_label(self.mappingQueue)); + RKLogDebug(@"Beginning object mapping activities within GCD queue labeled: %@", context); NSError *error = nil; _result = [[self performMapping:&error] retain]; NSAssert(_result || error, @"Expected performMapping to return a mapping result or an error."); @@ -276,7 +273,7 @@ - (void)performMappingInDispatchQueue { } [pool drain]; - }); + }]; } - (BOOL)canParseMIMEType:(NSString*)MIMEType { @@ -420,60 +417,49 @@ - (void)didFailLoadWithError:(NSError *)error { // NOTE: We do NOT call super here. We are overloading the default behavior from RKRequest - (void)didFinishLoad:(RKResponse*)response { - NSAssert([NSThread isMainThread], @"RKObjectLoaderDelegate callbacks must occur on the main thread"); - self.response = response; + [[NSManagedObjectContext contextForBackgroundThread] performBlock:^{ + self.response = response; - if ((_cachePolicy & RKRequestCachePolicyEtag) && [response isNotModified]) { - self.response = [self.cache responseForRequest:self]; - NSAssert(self.response, @"Unexpectedly loaded nil response from cache"); - [self updateInternalCacheDate]; - } + if ((_cachePolicy & RKRequestCachePolicyEtag) && [response isNotModified]) { + self.response = [self.cache responseForRequest:self]; + NSAssert(self.response, @"Unexpectedly loaded nil response from cache"); + [self updateInternalCacheDate]; + } - if (![self.response wasLoadedFromCache] && [self.response isSuccessful] && (_cachePolicy != RKRequestCachePolicyNone)) { - [self.cache storeResponse:self.response forRequest:self]; - } + if (![self.response wasLoadedFromCache] && [self.response isSuccessful] && (_cachePolicy != RKRequestCachePolicyNone)) { + [self.cache storeResponse:self.response forRequest:self]; + } - if ([_delegate respondsToSelector:@selector(request:didLoadResponse:)]) { - [_delegate request:self didLoadResponse:self.response]; - } + if ([_delegate respondsToSelector:@selector(request:didLoadResponse:)]) { + [_delegate request:self didLoadResponse:self.response]; + } - if (self.onDidLoadResponse) { - self.onDidLoadResponse(self.response); - } + if (self.onDidLoadResponse) { + self.onDidLoadResponse(self.response); + } - // Post the notification - NSDictionary* userInfo = [NSDictionary dictionaryWithObject:self.response - forKey:RKRequestDidLoadResponseNotificationUserInfoResponseKey]; - [[NSNotificationCenter defaultCenter] postNotificationName:RKRequestDidLoadResponseNotification - object:self - userInfo:userInfo]; - - if ([self isResponseMappable]) { - // Determine if we are synchronous here or not. - if (_sentSynchronously) { - NSError* error = nil; - _result = [[self performMapping:&error] retain]; - if (self.result) { - [self processMappingResult:self.result]; + // Post the notification + NSDictionary* userInfo = [NSDictionary dictionaryWithObject:self.response + forKey:RKRequestDidLoadResponseNotificationUserInfoResponseKey]; + [[NSNotificationCenter defaultCenter] postNotificationName:RKRequestDidLoadResponseNotification + object:self + userInfo:userInfo]; + + if ([self isResponseMappable]) { + // Determine if we are synchronous here or not. + if (_sentSynchronously) { + NSError* error = nil; + _result = [[self performMapping:&error] retain]; + if (self.result) { + [self processMappingResult:self.result]; + } else { + [self performSelectorInBackground:@selector(didFailLoadWithError:) withObject:error]; + } } else { - [self performSelectorInBackground:@selector(didFailLoadWithError:) withObject:error]; + [self performMappingInDispatchQueue]; } - } else { - [self performMappingInDispatchQueue]; } - } -} - -- (void)setMappingQueue:(dispatch_queue_t)newMappingQueue { - if (_mappingQueue) { - dispatch_release(_mappingQueue); - _mappingQueue = nil; - } - - if (newMappingQueue) { - dispatch_retain(newMappingQueue); - _mappingQueue = newMappingQueue; - } + }]; } // Proxy the delegate property back to our superclass implementation. The object loader should diff --git a/Code/ObjectMapping/RKObjectManager.m b/Code/ObjectMapping/RKObjectManager.m index 846ffb3318..82d7d55992 100644 --- a/Code/ObjectMapping/RKObjectManager.m +++ b/Code/ObjectMapping/RKObjectManager.m @@ -24,6 +24,7 @@ #import "RKManagedObjectLoader.h" #import "Support.h" #import "RKErrorMessage.h" +#import "NSManagedObject+ActiveRecord.h" NSString* const RKObjectManagerDidBecomeOfflineNotification = @"RKDidEnterOfflineModeNotification"; NSString* const RKObjectManagerDidBecomeOnlineNotification = @"RKDidEnterOnlineModeNotification"; diff --git a/Code/UI/RKFetchedResultsTableController.m b/Code/UI/RKFetchedResultsTableController.m index c564f99d58..a583934b05 100755 --- a/Code/UI/RKFetchedResultsTableController.m +++ b/Code/UI/RKFetchedResultsTableController.m @@ -244,7 +244,7 @@ - (void)loadTable { } _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest - managedObjectContext:[NSManagedObjectContext contextForCurrentThread] + managedObjectContext:[NSManagedObjectContext contextForMainThread] sectionNameKeyPath:_sectionNameKeyPath cacheName:_cacheName]; _fetchedResultsController.delegate = self; diff --git a/RestKit.xcodeproj/project.pbxproj b/RestKit.xcodeproj/project.pbxproj index f795a00db2..daa5bb9c9c 100644 --- a/RestKit.xcodeproj/project.pbxproj +++ b/RestKit.xcodeproj/project.pbxproj @@ -2509,6 +2509,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 25160D0B14564E810060A5C5; @@ -3194,7 +3195,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Code/Support/RestKit-Prefix.pch"; HEADER_SEARCH_PATHS = "${SDKROOT}/usr/include/libxml2"; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; OBJROOT = "$(SRCROOT)/Build"; OTHER_LDFLAGS = ( "-ObjC", @@ -3215,7 +3216,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Code/Support/RestKit-Prefix.pch"; HEADER_SEARCH_PATHS = "${SDKROOT}/usr/include/libxml2"; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; OBJROOT = "$(SRCROOT)/Build"; OTHER_LDFLAGS = ( "-ObjC", diff --git a/Tests/Logic/CoreData/NSManagedObject+ActiveRecordTest.m b/Tests/Logic/CoreData/NSManagedObject+ActiveRecordTest.m index 7c5c1a4c5b..c7209e4fb8 100644 --- a/Tests/Logic/CoreData/NSManagedObject+ActiveRecordTest.m +++ b/Tests/Logic/CoreData/NSManagedObject+ActiveRecordTest.m @@ -33,7 +33,7 @@ - (void)testFindByPrimaryKey - (void)testFindByPrimaryKeyInContext { RKManagedObjectStore *store = [RKTestFactory managedObjectStore]; - NSManagedObjectContext *context = [[RKTestFactory managedObjectStore] newManagedObjectContext]; + NSManagedObjectContext *context = [[RKTestFactory managedObjectStore] newMainManagedObjectContext]; NSEntityDescription *entity = [RKHuman entityDescription]; entity.primaryKeyAttributeName = @"railsID"; diff --git a/Tests/Logic/CoreData/RKManagedObjectStoreTest.m b/Tests/Logic/CoreData/RKManagedObjectStoreTest.m index 6424ab5fe7..70f810c0cf 100644 --- a/Tests/Logic/CoreData/RKManagedObjectStoreTest.m +++ b/Tests/Logic/CoreData/RKManagedObjectStoreTest.m @@ -31,7 +31,7 @@ @implementation RKManagedObjectStoreTest - (void)testInstantiationOfNewManagedObjectContextAssociatesWithObjectStore { RKManagedObjectStore *store = [RKTestFactory managedObjectStore]; - NSManagedObjectContext *context = [store newManagedObjectContext]; + NSManagedObjectContext *context = [store newMainManagedObjectContext]; assertThat([context managedObjectStore], is(equalTo(store))); } diff --git a/Tests/Logic/CoreData/RKManagedObjectThreadSafeInvocationTest.m b/Tests/Logic/CoreData/RKManagedObjectThreadSafeInvocationTest.m index 1c9fc704f6..723b86111a 100644 --- a/Tests/Logic/CoreData/RKManagedObjectThreadSafeInvocationTest.m +++ b/Tests/Logic/CoreData/RKManagedObjectThreadSafeInvocationTest.m @@ -131,7 +131,7 @@ - (void)createBackgroundObjects { [invocation setSelector:@selector(informDelegateWithDictionary:)]; [invocation setArgument:&_dictionary atIndex:2]; // NOTE: _cmd and self are 0 and 1 [invocation setManagedObjectKeyPaths:[NSSet setWithObject:@"humans"] forArgument:2]; - [invocation invokeOnMainThread]; + [invocation invoke]; [pool drain]; }