Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'feature/635-accelerate-entity-cache' into development

  • Loading branch information...
commit 2c704e6fdcdcf05c49612a515c9bf083837557b5 2 parents 7656a96 + 9bf89eb
@blakewatters blakewatters authored
Showing with 4,112 additions and 732 deletions.
  1. +31 −1 Code/CoreData/NSEntityDescription+RKAdditions.h
  2. +34 −2 Code/CoreData/NSEntityDescription+RKAdditions.m
  3. +180 −0 Code/CoreData/RKEntityByAttributeCache.h
  4. +279 −0 Code/CoreData/RKEntityByAttributeCache.m
  5. +133 −0 Code/CoreData/RKEntityCache.h
  6. +141 −0 Code/CoreData/RKEntityCache.m
  7. +12 −4 Code/CoreData/RKFetchRequestManagedObjectCache.m
  8. +0 −83 Code/CoreData/RKInMemoryEntityCache.h
  9. +0 −291 Code/CoreData/RKInMemoryEntityCache.m
  10. +0 −1  Code/CoreData/RKInMemoryManagedObjectCache.h
  11. +47 −13 Code/CoreData/RKInMemoryManagedObjectCache.m
  12. +25 −0 Code/CoreData/RKManagedObjectCaching.h
  13. +18 −3 Code/CoreData/RKManagedObjectMapping.m
  14. +1 −1  Code/CoreData/RKManagedObjectMappingOperation.m
  15. +2 −3 Code/CoreData/RKManagedObjectStore.m
  16. +121 −0 Code/Support/RKBenchmark.h
  17. +119 −0 Code/Support/RKBenchmark.m
  18. +1 −0  Code/Support/Support.h
  19. +1 −0  Code/Support/lcl_config_components.h
  20. +3 −2 Gemfile
  21. +8 −4 Gemfile.lock
  22. +22 −0 Rakefile
  23. +60 −18 RestKit.xcodeproj/project.pbxproj
  24. +2,062 −0 Tests/Fixtures/JSON/benchmark_parents_and_children.json
  25. +40 −0 Tests/Fixtures/JSON/parents_and_children.json
  26. +18 −0 Tests/Logic/CoreData/NSEntityDescription+RKAdditionsTest.m
  27. +378 −0 Tests/Logic/CoreData/RKEntityByAttributeCacheTest.m
  28. +175 −0 Tests/Logic/CoreData/RKEntityCacheTest.m
  29. +3 −3 Tests/Logic/CoreData/RKFetchRequestMappingCacheTest.m
  30. +0 −296 Tests/Logic/CoreData/RKInMemoryEntityCacheTest.m
  31. +129 −1 Tests/Logic/CoreData/RKManagedObjectMappingOperationTest.m
  32. +69 −6 Tests/Logic/CoreData/RKManagedObjectMappingTest.m
  33. BIN  Tests/Models/Data Model.xcdatamodel/elements
  34. BIN  Tests/Models/Data Model.xcdatamodel/layout
View
32 Code/CoreData/NSEntityDescription+RKAdditions.h
@@ -17,6 +17,14 @@
extern NSString * const RKEntityDescriptionPrimaryKeyAttributeUserInfoKey;
/**
+ The substitution variable used in predicateForPrimaryKeyAttribute.
+
+ **Value**: @"PRIMARY_KEY_VALUE"
+ @see predicateForPrimaryKeyAttribute
+ */
+extern NSString * const RKEntityDescriptionPrimaryKeyAttributeValuePredicateSubstitutionVariable;
+
+/**
Provides extensions to NSEntityDescription for various common tasks.
*/
@interface NSEntityDescription (RKAdditions)
@@ -34,6 +42,28 @@ extern NSString * const RKEntityDescriptionPrimaryKeyAttributeUserInfoKey;
Programmatically configured values take precedence over the user info
dictionary.
*/
-@property(nonatomic, retain) NSString *primaryKeyAttribute;
+@property (nonatomic, retain) NSString *primaryKeyAttribute;
+
+/**
+ Returns a cached predicate specifying that the primary key attribute is equal to the $PRIMARY_KEY_VALUE
+ substitution variable.
+
+ This predicate is cached to avoid parsing overhead during object mapping operations
+ and must be evaluated using [NSPredicate predicateWithSubstitutionVariables:]
+
+ @return A cached predicate specifying the value of the primary key attribute is equal to the $PRIMARY_KEY_VALUE
+ substitution variable.
+ */
+- (NSPredicate *)predicateForPrimaryKeyAttribute;
+
+/**
+ Returns a predicate specifying that the value of the primary key attribute is equal to a given
+ value. This predicate is constructed by evaluating the cached predicate returned by the
+ predicateForPrimaryKeyAttribute with a dictionary of substitution variables specifying that
+ $PRIMARY_KEY_VALUE is equal to the given value.
+
+ @return A predicate speciying that the value of the primary key attribute is equal to a given value.
+ */
+- (NSPredicate *)predicateForPrimaryKeyAttributeWithValue:(id)value;
@end
View
36 Code/CoreData/NSEntityDescription+RKAdditions.m
@@ -10,10 +10,23 @@
#import "NSEntityDescription+RKAdditions.h"
NSString * const RKEntityDescriptionPrimaryKeyAttributeUserInfoKey = @"primaryKeyAttribute";
-static char primaryKeyAttributeKey;
+NSString * const RKEntityDescriptionPrimaryKeyAttributeValuePredicateSubstitutionVariable = @"PRIMARY_KEY_VALUE";
+
+static char primaryKeyAttributeKey, primaryKeyPredicateKey;
@implementation NSEntityDescription (RKAdditions)
+- (void)setPredicateForPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
+{
+ NSPredicate *predicate = [NSPredicate predicateWithFormat:@"%K == $PRIMARY_KEY_VALUE", primaryKeyAttribute];
+ objc_setAssociatedObject(self,
+ &primaryKeyPredicateKey,
+ predicate,
+ OBJC_ASSOCIATION_RETAIN);
+}
+
+#pragma mark - Public
+
- (NSString *)primaryKeyAttribute
{
// Check for an associative object reference
@@ -22,6 +35,11 @@ - (NSString *)primaryKeyAttribute
// Fall back to the userInfo dictionary
if (! primaryKeyAttribute) {
primaryKeyAttribute = [self.userInfo valueForKey:RKEntityDescriptionPrimaryKeyAttributeUserInfoKey];
+
+ // If we have loaded from the user info, ensure we have a predicate
+ if (! [self predicateForPrimaryKeyAttribute]) {
+ [self setPredicateForPrimaryKeyAttribute:primaryKeyAttribute];
+ }
}
return primaryKeyAttribute;
@@ -32,7 +50,21 @@ - (void)setPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
objc_setAssociatedObject(self,
&primaryKeyAttributeKey,
primaryKeyAttribute,
- OBJC_ASSOCIATION_RETAIN);
+ OBJC_ASSOCIATION_RETAIN);
+ [self setPredicateForPrimaryKeyAttribute:primaryKeyAttribute];
+}
+
+
+- (NSPredicate *)predicateForPrimaryKeyAttribute
+{
+ return (NSPredicate *) objc_getAssociatedObject(self, &primaryKeyPredicateKey);
+}
+
+- (NSPredicate *)predicateForPrimaryKeyAttributeWithValue:(id)value
+{
+ NSDictionary *variables = [NSDictionary dictionaryWithObject:value
+ forKey:RKEntityDescriptionPrimaryKeyAttributeValuePredicateSubstitutionVariable];
+ return [[self predicateForPrimaryKeyAttribute] predicateWithSubstitutionVariables:variables];
}
@end
View
180 Code/CoreData/RKEntityByAttributeCache.h
@@ -0,0 +1,180 @@
+//
+// RKEntityByAttributeCache.h
+// RestKit
+//
+// Created by Blake Watters on 5/1/12.
+// Copyright (c) 2012 RestKit. All rights reserved.
+//
+
+#import <CoreData/CoreData.h>
+
+/**
+ Instances of RKEntityByAttributeCache provide an in-memory caching mechanism
+ for managed objects instances of an entity in a managed object context with
+ the value of one of the object's attributes acting as the cache key. When loaded,
+ the cache will retrieve all instances of an entity from the store and build a
+ dictionary mapping values for the given cache key attribute to the managed object
+ ID for all objects matching the value. The cache can then be used to quickly retrieve
+ objects by attribute value for the cache key without executing another fetch request
+ against the managed object context. This can provide a large performance improvement
+ when a large number of objects are being retrieved using a particular attribute as
+ the key.
+
+ RKEntityByAttributeCache instances are used by the RKEntityCache to provide
+ caching for multiple entities at once.
+
+ @see RKEntityCache
+ */
+@interface RKEntityByAttributeCache : NSObject
+
+///-----------------------------------------------------------------------------
+/// @name Creating a Cache
+///-----------------------------------------------------------------------------
+
+/**
+ Initializes the receiver with a given entity, attribute, and managed object context.
+
+ @param entity The Core Data entity description for the managed objects being cached.
+ @param attributeName The name of an attribute within the cached entity that acts as the cache key.
+ @param managedObjectContext The managed object context the cache retrieves the cached
+ objects from
+ @return The receiver, initialized with the given entity, attribute, and managed object
+ context.
+ */
+- (id)initWithEntity:(NSEntityDescription *)entity attribute:(NSString *)attributeName managedObjectContext:(NSManagedObjectContext *)context;
+
+///-----------------------------------------------------------------------------
+/// @name Getting Cache Identity
+///-----------------------------------------------------------------------------
+
+/**
+ The Core Data entity description for the managed objects being cached.
+ */
+@property (nonatomic, readonly) NSEntityDescription *entity;
+
+/**
+ An attribute that is part of the cached entity that acts as the cache key.
+ */
+@property (nonatomic, readonly) NSString *attribute;
+
+/**
+ The managed object context the receiver fetches cached objects from.
+ */
+@property (nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
+
+/**
+ A Boolean value determining if the receiever monitors the managed object context
+ for changes and updates the cache entries using the notifications emitted.
+ */
+@property (nonatomic, assign) BOOL monitorsContextForChanges;
+
+///-----------------------------------------------------------------------------
+/// @name Loading and Flushing the Cache
+///-----------------------------------------------------------------------------
+
+/**
+ Loads the cache by finding all instances of the configured entity and building
+ an association between the value of the cached attribute's value and the
+ managed object ID for the object.
+ */
+- (void)load;
+
+/**
+ Flushes the cache by releasing all cache attribute value to managed object ID
+ associations.
+ */
+- (void)flush;
+
+///-----------------------------------------------------------------------------
+/// @name Inspecting Cache State
+///-----------------------------------------------------------------------------
+
+/**
+ A Boolean value indicating if the cache has loaded associations between cache
+ attribute values and managed object ID's.
+ */
+- (BOOL)isLoaded;
+
+/**
+ Returns a count of the total number of cached objects.
+ */
+- (NSUInteger)count;
+
+/**
+ Returns the total number of cached objects with a given value for
+ the attribute acting as the cache key.
+
+ @param attributeValue The value for the cache key attribute to retrieve
+ a count of the objects with a matching value.
+ @return The number of objects in the cache with the given value for the cache
+ attribute of the receiver.
+ */
+- (NSUInteger)countWithAttributeValue:(id)attributeValue;
+
+/**
+ Returns the number of unique attribute values contained within the receiver.
+
+ @return The number of unique attribute values within the receiver.
+ */
+- (NSUInteger)countOfAttributeValues;
+
+/**
+ Returns a Boolean value that indicates whether a given object is present
+ in the cache.
+
+ @param object An object.
+ @return YES if object is present in the cache, otherwise NO.
+ */
+- (BOOL)containsObject:(NSManagedObject *)object;
+
+/**
+ Returns a Boolean value that indicates whether one of more objects is present
+ in the cache with a given value of the cache key attribute.
+
+ @param attributeValue The value with which to check the cache for objects with
+ a matching value.
+ @return YES if one or more objects with the given value for the cache key
+ attribute is present in the cache, otherwise NO.
+ */
+- (BOOL)containsObjectWithAttributeValue:(id)attributeValue;
+
+/**
+ Returns the first object with a matching value for the cache key attribute.
+
+ @param attributeValue A value for the cache key attribute.
+ @return An object with the value of attribute matching attributeValue or nil.
+ */
+- (NSManagedObject *)objectWithAttributeValue:(id)attributeValue;
+
+/**
+ Returns the collection of objects with a matching value for the cache key attribute.
+
+ @param attributeValue A value for the cache key attribute.
+ @return An array of objects with the value of attribute matching attributeValue or
+ an empty array.
+ */
+- (NSArray *)objectsWithAttributeValue:(id)attributeValue;
+
+///-----------------------------------------------------------------------------
+/// @name Managing Cached Objects
+///-----------------------------------------------------------------------------
+
+/**
+ Adds a managed object to the cache.
+
+ The object must be an instance of the cached entity.
+
+ @param object The managed object to add to the cache.
+ */
+- (void)addObject:(NSManagedObject *)object;
+
+/**
+ Removes a managed object from the cache.
+
+ The object must be an instance of the cached entity.
+
+ @param object The managed object to remove from the cache.
+ */
+- (void)removeObject:(NSManagedObject *)object;
+
+@end
View
279 Code/CoreData/RKEntityByAttributeCache.m
@@ -0,0 +1,279 @@
+//
+// RKEntityByAttributeCache.m
+// RestKit
+//
+// Created by Blake Watters on 5/1/12.
+// Copyright (c) 2012 RestKit. All rights reserved.
+//
+
+#if TARGET_OS_IPHONE
+#import <UIKit/UIKit.h>
+#endif
+
+#import "RKEntityByAttributeCache.h"
+#import "RKLog.h"
+#import "RKObjectPropertyInspector.h"
+#import "RKObjectPropertyInspector+CoreData.h"
+
+// Set Logging Component
+#undef RKLogComponent
+#define RKLogComponent lcl_cRestKitCoreDataCache
+
+@interface RKEntityByAttributeCache ()
+@property (nonatomic, retain) NSMutableDictionary *attributeValuesToObjectIDs;
+@end
+
+@implementation RKEntityByAttributeCache
+
+@synthesize entity = _entity;
+@synthesize attribute = _attribute;
+@synthesize managedObjectContext = _managedObjectContext;
+@synthesize attributeValuesToObjectIDs = _attributeValuesToObjectIDs;
+@synthesize monitorsContextForChanges = _monitorsContextForChanges;
+
+- (id)initWithEntity:(NSEntityDescription *)entity attribute:(NSString *)attributeName managedObjectContext:(NSManagedObjectContext *)context
+{
+ self = [self init];
+ if (self) {
+ _entity = [entity retain];
+ _attribute = [attributeName retain];
+ _managedObjectContext = [context retain];
+ _monitorsContextForChanges = YES;
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(managedObjectContextDidChange:)
+ name:NSManagedObjectContextObjectsDidChangeNotification
+ object:context];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(managedObjectContextDidSave:)
+ name:NSManagedObjectContextDidSaveNotification
+ object:context];
+#if TARGET_OS_IPHONE
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(didReceiveMemoryWarning:)
+ name:UIApplicationDidReceiveMemoryWarningNotification
+ object:nil];
+#endif
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+
+ [_entity release];
+ [_attribute release];
+ [_managedObjectContext release];
+ [_attributeValuesToObjectIDs release];
+
+ [super dealloc];
+}
+
+- (NSUInteger)count
+{
+ return [[[self.attributeValuesToObjectIDs allValues] valueForKeyPath:@"@sum.@count"] integerValue];
+}
+
+- (NSUInteger)countOfAttributeValues
+{
+ return [self.attributeValuesToObjectIDs count];
+}
+
+- (NSUInteger)countWithAttributeValue:(id)attributeValue
+{
+ return [[self objectsWithAttributeValue:attributeValue] count];
+}
+
+- (BOOL)shouldCoerceAttributeToString:(NSString *)attributeValue
+{
+ if ([attributeValue isKindOfClass:[NSString class]] || [attributeValue isEqual:[NSNull null]]) {
+ return NO;
+ }
+
+ Class attributeType = [[RKObjectPropertyInspector sharedInspector] typeForProperty:self.attribute ofEntity:self.entity];
+ return [attributeType instancesRespondToSelector:@selector(stringValue)];
+}
+
+- (void)load
+{
+ RKLogInfo(@"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];
+ if (error) {
+ RKLogError(@"Failed to load entity cache: %@", error);
+ return;
+ }
+ [fetchRequest release];
+
+ 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
+{
+ RKLogInfo(@"Flushing entity cache for Entity '%@' by attribute '%@'", self.entity.name, self.attribute);
+ self.attributeValuesToObjectIDs = nil;
+}
+
+- (void)reload
+{
+ [self flush];
+ [self load];
+}
+
+- (BOOL)isLoaded
+{
+ return (self.attributeValuesToObjectIDs != nil);
+}
+
+- (NSManagedObject *)objectWithAttributeValue:(id)attributeValue
+{
+ NSArray *objects = [self objectsWithAttributeValue:attributeValue];
+ return ([objects count] > 0) ? [objects objectAtIndex:0] : nil;
+}
+
+- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID {
+ /*
+ NOTE:
+ We use existingObjectWithID: as opposed to objectWithID: as objectWithID: can return us a fault
+ that will raise an exception when fired. existingObjectWithID:error: will return nil if the ID has been
+ deleted. objectRegisteredForID: is also an acceptable approach.
+ */
+ NSError *error = nil;
+ NSManagedObject *object = [self.managedObjectContext existingObjectWithID:objectID error:&error];
+ if (! object && error) {
+ RKLogError(@"Failed to retrieve managed object with ID %@. Error %@\n%@", objectID, [error localizedDescription], [error userInfo]);
+ return nil;
+ }
+
+ return object;
+}
+
+- (NSArray *)objectsWithAttributeValue:(id)attributeValue
+{
+ attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
+ NSMutableArray *objectIDs = [self.attributeValuesToObjectIDs objectForKey:attributeValue];
+ if (objectIDs) {
+ NSMutableArray *objects = [NSMutableArray arrayWithCapacity:[objectIDs count]];
+ for (NSManagedObjectID *objectID in objectIDs) {
+ NSManagedObject *object = [self objectWithID:objectID];
+ if (object) [objects addObject:object];
+ }
+
+ return objects;
+ }
+
+ return [NSArray array];
+}
+
+- (void)addObject:(NSManagedObject *)object
+{
+ NSAssert([object.entity isEqual:self.entity], @"Cannot add object with entity '%@' to cache with entity of '%@'", [[object entity] name], [self.entity name]);
+ id attributeValue = [object valueForKey:self.attribute];
+ // Coerce to a string if possible
+ attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
+ if (attributeValue) {
+ NSManagedObjectID *objectID = [object objectID];
+ NSMutableArray *objectIDs = [self.attributeValuesToObjectIDs objectForKey:attributeValue];
+ if (objectIDs) {
+ if (! [objectIDs containsObject:objectID]) {
+ [objectIDs addObject:objectID];
+ }
+ } else {
+ objectIDs = [NSMutableArray arrayWithObject:objectID];
+ }
+
+ if (nil == self.attributeValuesToObjectIDs) self.attributeValuesToObjectIDs = [NSMutableDictionary dictionary];
+ [self.attributeValuesToObjectIDs setValue:objectIDs forKey:attributeValue];
+ } else {
+ RKLogWarning(@"Unable to add object with nil value for attribute '%@': %@", self.attribute, object);
+ }
+}
+
+- (void)removeObject:(NSManagedObject *)object
+{
+ NSAssert([object.entity isEqual:self.entity], @"Cannot remove object with entity '%@' from cache with entity of '%@'", [[object entity] name], [self.entity name]);
+ id attributeValue = [object valueForKey:self.attribute];
+ // Coerce to a string if possible
+ attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
+ if (attributeValue) {
+ NSManagedObjectID *objectID = [object objectID];
+ NSMutableArray *objectIDs = [self.attributeValuesToObjectIDs objectForKey:attributeValue];
+ if (objectIDs && [objectIDs containsObject:objectID]) {
+ [objectIDs removeObject:objectID];
+ }
+ } else {
+ RKLogWarning(@"Unable to remove object with nil value for attribute '%@': %@", self.attribute, object);
+ }
+}
+
+- (BOOL)containsObjectWithAttributeValue:(id)attributeValue
+{
+ // Coerce to a string if possible
+ attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
+ return [[self objectsWithAttributeValue:attributeValue] count] > 0;
+}
+
+- (BOOL)containsObject:(NSManagedObject *)object
+{
+ if (! [object.entity isEqual:self.entity]) return NO;
+ id attributeValue = [object valueForKey:self.attribute];
+ // Coerce to a string if possible
+ attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
+ return [[self objectsWithAttributeValue:attributeValue] containsObject:object];
+}
+
+- (void)managedObjectContextDidChange:(NSNotification *)notification
+{
+ if (self.monitorsContextForChanges == NO) return;
+
+ NSDictionary *userInfo = notification.userInfo;
+ NSSet *insertedObjects = [userInfo objectForKey:NSInsertedObjectsKey];
+ NSSet *updatedObjects = [userInfo objectForKey:NSUpdatedObjectsKey];
+ NSSet *deletedObjects = [userInfo objectForKey:NSDeletedObjectsKey];
+ RKLogTrace(@"insertedObjects=%@, updatedObjects=%@, deletedObjects=%@", insertedObjects, updatedObjects, deletedObjects);
+
+ NSMutableSet *objectsToAdd = [NSMutableSet setWithSet:insertedObjects];
+ [objectsToAdd unionSet:updatedObjects];
+
+ for (NSManagedObject *object in objectsToAdd) {
+ if ([object.entity isEqual:self.entity]) {
+ [self addObject:object];
+ }
+ }
+
+ for (NSManagedObject *object in deletedObjects) {
+ if ([object.entity isEqual:self.entity]) {
+ [self removeObject:object];
+ }
+ }
+}
+
+- (void)managedObjectContextDidSave:(NSNotification *)notification
+{
+ // After the MOC has been saved, we flush to ensure any temporary
+ // objectID references are converted into permanent ID's on the next load.
+ [self flush];
+}
+
+- (void)didReceiveMemoryWarning:(NSNotification *)notification
+{
+ [self flush];
+}
+
+@end
View
133 Code/CoreData/RKEntityCache.h
@@ -0,0 +1,133 @@
+//
+// RKEntityCache.h
+// RestKit
+//
+// Created by Blake Watters on 5/2/12.
+// Copyright (c) 2012 RestKit. All rights reserved.
+//
+
+#import <CoreData/CoreData.h>
+
+@class RKEntityByAttributeCache;
+
+/**
+ Instances of RKInMemoryEntityCache provide an in-memory caching mechanism for
+ objects in a Core Data managed object context. Managed objects can be cached by
+ attribute for fast retrieval without repeatedly hitting the Core Data persistent store.
+ This can provide a substantial speed advantage over issuing fetch requests
+ in cases where repeated look-ups of the same data are performed using a small set
+ of attributes as the query key. Internally, the cache entries are maintained as
+ references to the NSManagedObjectID of corresponding cached objects.
+ */
+@interface RKEntityCache : NSObject
+
+///-----------------------------------------------------------------------------
+/// @name Initializing the Cache
+///-----------------------------------------------------------------------------
+
+/**
+ Initializes the receiver with a managed object context containing the entity instances to be cached.
+
+ @param context The managed object context containing objects to be cached.
+ @returns self, initialized with context.
+ */
+- (id)initWithManagedObjectContext:(NSManagedObjectContext *)context;
+
+/**
+ The managed object context with which the receiver is associated.
+ */
+@property (nonatomic, retain, readonly) NSManagedObjectContext *managedObjectContext;
+
+///-----------------------------------------------------------------------------
+/// @name Caching Objects by Attribute
+///-----------------------------------------------------------------------------
+
+/**
+ Caches all instances of an entity using the value for an attribute as the cache key.
+
+ @param entity The entity to cache all instances of.
+ @param attributeName The attribute to cache the instances by.
+ */
+- (void)cacheObjectsForEntity:(NSEntityDescription *)entity byAttribute:(NSString *)attributeName;
+
+/**
+ Returns a Boolean value indicating if all instances of an entity have been cached by a given attribute name.
+
+ @param entity The entity to check the cache status of.
+ @param attributeName The attribute to check the cache status with.
+ @return YES if the cache has been loaded with instances with the given attribute, else NO.
+ */
+- (BOOL)isEntity:(NSEntityDescription *)entity cachedByAttribute:(NSString *)attributeName;
+
+/**
+ Retrieves the first cached instance of a given entity where the specified attribute matches the given value.
+
+ @param entity The entity to search the cache for instances of.
+ @param attributeName The attribute to search the cache for matches with.
+ @param attributeValue The value of the attribute to return a match for.
+ @return A matching managed object instance or nil.
+ @raise NSInvalidArgumentException Raised if instances of the entity and attribute have not been cached.
+ */
+- (NSManagedObject *)objectForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue;
+
+/**
+ Retrieves all cached instances of a given entity where the specified attribute matches the given value.
+
+ @param entity The entity to search the cache for instances of.
+ @param attributeName The attribute to search the cache for matches with.
+ @param attributeValue The value of the attribute to return a match for.
+ @return All matching managed object instances or nil.
+ @raise NSInvalidArgumentException Raised if instances of the entity and attribute have not been cached.
+ */
+- (NSArray *)objectsForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue;
+
+///-----------------------------------------------------------------------------
+// @name Accessing Underlying Caches
+///-----------------------------------------------------------------------------
+
+/**
+ Retrieves the underlying entity attribute cache for a given entity and attribute.
+
+ @param entity The entity to retrieve the entity attribute cache object for.
+ @param attributeName The attribute to retrieve the entity attribute cache object for.
+ @return The entity attribute cache for the given entity and attribute, or nil if none was found.
+ */
+- (RKEntityByAttributeCache *)attributeCacheForEntity:(NSEntityDescription *)entity attribute:(NSString *)attributeName;
+
+/**
+ Retrieves all entity attributes caches for a given entity.
+
+ @param entity The entity to retrieve the collection of entity attribute caches for.
+ @return An array of entity attribute cache objects for the given entity or an empty array if none were found.
+ */
+- (NSArray *)attributeCachesForEntity:(NSEntityDescription *)entity;
+
+///-----------------------------------------------------------------------------
+// @name Managing the Cache
+///-----------------------------------------------------------------------------
+
+/**
+ Flushes the entity cache by sending a flush message to each entity attribute cache
+ contained within the receiver.
+
+ @see [RKEntityByAttributeCache flush]
+ */
+- (void)flush;
+
+/**
+ Adds a given object to all entity attribute caches for the object's entity contained
+ within the receiver.
+
+ @param object The object to add to the appropriate entity attribute caches.
+ */
+- (void)addObject:(NSManagedObject *)object;
+
+/**
+ Removed a given object from all entity attribute caches for the object's entity contained
+ within the receiver.
+
+ @param object The object to remove from the appropriate entity attribute caches.
+ */
+- (void)removeObject:(NSManagedObject *)object;
+
+@end
View
141 Code/CoreData/RKEntityCache.m
@@ -0,0 +1,141 @@
+//
+// RKEntityCache.m
+// RestKit
+//
+// Created by Blake Watters on 5/2/12.
+// Copyright (c) 2012 RestKit. All rights reserved.
+//
+
+#import "RKEntityCache.h"
+#import "RKEntityByAttributeCache.h"
+
+@interface RKEntityCache ()
+@property (nonatomic, retain) NSMutableSet *attributeCaches;
+@end
+
+@implementation RKEntityCache
+
+@synthesize managedObjectContext = _managedObjectContext;
+@synthesize attributeCaches = _attributeCaches;
+
+- (id)initWithManagedObjectContext:(NSManagedObjectContext *)context
+{
+ NSAssert(context, @"Cannot initialize entity cache with a nil context");
+ self = [super init];
+ if (self) {
+ _managedObjectContext = [context retain];
+ _attributeCaches = [[NSMutableSet alloc] init];
+ }
+
+ return self;
+}
+
+- (id)init
+{
+ return [self initWithManagedObjectContext:nil];
+}
+
+- (void)dealloc
+{
+ [_managedObjectContext release];
+ [_attributeCaches release];
+ [super dealloc];
+}
+
+- (void)cacheObjectsForEntity:(NSEntityDescription *)entity byAttribute:(NSString *)attributeName
+{
+ NSAssert(entity, @"Cannot cache objects for a nil entity");
+ NSAssert(attributeName, @"Cannot cache objects without an attribute");
+ RKEntityByAttributeCache *attributeCache = [self attributeCacheForEntity:entity attribute:attributeName];
+ if (attributeCache && !attributeCache.isLoaded) {
+ [attributeCache load];
+ } else {
+ attributeCache = [[RKEntityByAttributeCache alloc] initWithEntity:entity attribute:attributeName managedObjectContext:self.managedObjectContext];
+ [attributeCache load];
+ [self.attributeCaches addObject:attributeCache];
+ [attributeCache release];
+ }
+}
+
+- (BOOL)isEntity:(NSEntityDescription *)entity cachedByAttribute:(NSString *)attributeName
+{
+ NSAssert(entity, @"Cannot check cache status for a nil entity");
+ NSAssert(attributeName, @"Cannot check cache status for a nil attribute");
+ RKEntityByAttributeCache *attributeCache = [self attributeCacheForEntity:entity attribute:attributeName];
+ return (attributeCache && attributeCache.isLoaded);
+}
+
+- (NSManagedObject *)objectForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue
+{
+ NSAssert(entity, @"Cannot retrieve cached objects with a nil entity");
+ NSAssert(attributeName, @"Cannot retrieve cached objects by a nil entity");
+ RKEntityByAttributeCache *attributeCache = [self attributeCacheForEntity:entity attribute:attributeName];
+ if (attributeCache) {
+ return [attributeCache objectWithAttributeValue:attributeValue];
+ }
+
+ return nil;
+}
+
+- (NSArray *)objectsForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue
+{
+ NSAssert(entity, @"Cannot retrieve cached objects with a nil entity");
+ NSAssert(attributeName, @"Cannot retrieve cached objects by a nil entity");
+ RKEntityByAttributeCache *attributeCache = [self attributeCacheForEntity:entity attribute:attributeName];
+ if (attributeCache) {
+ return [attributeCache objectsWithAttributeValue:attributeValue];
+ }
+
+ return [NSSet set];
+}
+
+- (RKEntityByAttributeCache *)attributeCacheForEntity:(NSEntityDescription *)entity attribute:(NSString *)attributeName
+{
+ NSAssert(entity, @"Cannot retrieve attribute cache for a nil entity");
+ NSAssert(attributeName, @"Cannot retrieve attribute cache for a nil attribute");
+ for (RKEntityByAttributeCache *cache in self.attributeCaches) {
+ if ([cache.entity isEqual:entity] && [cache.attribute isEqualToString:attributeName]) {
+ return cache;
+ }
+ }
+
+ return nil;
+}
+
+- (NSSet *)attributeCachesForEntity:(NSEntityDescription *)entity
+{
+ NSAssert(entity, @"Cannot retrieve attribute caches for a nil entity");
+ NSMutableSet *set = [NSMutableSet set];
+ for (RKEntityByAttributeCache *cache in self.attributeCaches) {
+ if ([cache.entity isEqual:entity]) {
+ [set addObject:cache];
+ }
+ }
+
+ return [NSSet setWithSet:set];
+}
+
+- (void)flush
+{
+ [self.attributeCaches makeObjectsPerformSelector:@selector(flush)];
+}
+
+- (void)addObject:(NSManagedObject *)object
+{
+ NSAssert(object, @"Cannot add a nil object to the cache");
+ NSArray *attributeCaches = [self attributeCachesForEntity:object.entity];
+ for (RKEntityByAttributeCache *cache in attributeCaches) {
+ [cache addObject:object];
+ }
+}
+
+- (void)removeObject:(NSManagedObject *)object
+{
+ NSAssert(object, @"Cannot remove a nil object from the cache");
+ NSArray *attributeCaches = [self attributeCachesForEntity:object.entity];
+ for (RKEntityByAttributeCache *cache in attributeCaches) {
+ [cache removeObject:object];
+ }
+}
+
+@end
View
16 Code/CoreData/RKFetchRequestManagedObjectCache.m
@@ -39,10 +39,18 @@ - (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
}
}
- NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
- [fetchRequest setEntity:entity];
- [fetchRequest setFetchLimit:1];
- [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", primaryKeyAttribute, searchValue]];
+ // Use cached predicate if primary key matches
+ NSPredicate *predicate = nil;
+ if ([entity.primaryKeyAttribute isEqualToString:primaryKeyAttribute]) {
+ predicate = [entity predicateForPrimaryKeyAttributeWithValue:searchValue];
+ } else {
+ // Parse a predicate
+ predicate = [NSPredicate predicateWithFormat:@"%K = %@", primaryKeyAttribute, searchValue];
+ }
+ NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
+ fetchRequest.entity = entity;
+ fetchRequest.fetchLimit = 1;
+ fetchRequest.predicate = predicate;
NSArray *objects = [NSManagedObject executeFetchRequest:fetchRequest inContext:managedObjectContext];
RKLogDebug(@"Found objects '%@' using fetchRequest '%@'", objects, fetchRequest);
[fetchRequest release];
View
83 Code/CoreData/RKInMemoryEntityCache.h
@@ -1,83 +0,0 @@
-//
-// RKInMemoryEntityCache.h
-// RestKit
-//
-// Created by Jeff Arena on 1/24/12.
-// Copyright (c) 2009-2012 RestKit. All rights reserved.
-//
-
-#import <CoreData/CoreData.h>
-
-/**
- Instances of RKInMemoryEntityCache provide an in-memory caching mechanism for
- objects in a Core Data managed object context. Managed objects can be cached by
- attribute for fast retrieval without repeatedly hitting the Core Data persistent store.
- This can provide a substantial speed advantage over issuing fetch requests
- in cases where repeated look-ups of the same data are performed using a small set
- of attributes as the query key. Internally, the cache entries are maintained as
- references to the NSManagedObjectID of corresponding cached objects.
- */
-@interface RKInMemoryEntityCache : NSObject
-
-/**
- The managed object context from which objects will be cached.
- */
-@property(nonatomic, readonly) NSManagedObjectContext *managedObjectContext;
-
-/// @name Initializing the Cache
-
-/**
- Initializes the receiver with a managed object context containing the entity instances to be cached.
-
- @param context The managed object context containing objects to be cached.
- @returns self, initialized with context.
- */
-- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
-
-/// @name Caching Objects by Attribute
-
-/**
- Retrieves all objects within the cache
- */
-- (NSMutableDictionary *)cachedObjectsForEntity:(NSEntityDescription *)entity
- byAttribute:(NSString *)attributeName
- inContext:(NSManagedObjectContext *)managedObjectContext;
-
-/**
- */
-- (NSManagedObject *)cachedObjectForEntity:(NSEntityDescription *)entity
- withAttribute:(NSString *)attributeName
- value:(id)attributeValue
- inContext:(NSManagedObjectContext *)managedObjectContext;
-
-/**
- Caches all instances of an entity in a given managed object context by the value
- */
-- (void)cacheObjectsForEntity:(NSEntityDescription *)entity
- byAttribute:(NSString *)attributeName
- inContext:(NSManagedObjectContext *)managedObjectContext;
-
-/**
- */
-- (void)cacheObject:(NSManagedObject *)managedObject
- byAttribute:(NSString *)attributeName
- inContext:(NSManagedObjectContext *)managedObjectContext;
-
-/**
- */
-- (void)cacheObject:(NSEntityDescription *)entity
- byAttribute:(NSString *)attributeName
- value:(id)primaryKeyValue
- inContext:(NSManagedObjectContext *)managedObjectContext;
-
-/**
- */
-- (void)expireCacheEntryForObject:(NSManagedObject *)managedObject
- byAttribute:(NSString *)attributeName
- inContext:(NSManagedObjectContext *)managedObjectContext;
-
-/**
- */
-- (void)expireCacheEntriesForEntity:(NSEntityDescription *)entity;
-
-@end
View
291 Code/CoreData/RKInMemoryEntityCache.m
@@ -1,291 +0,0 @@
-//
-// RKInMemoryEntityCache.m
-// RestKit
-//
-// Created by Jeff Arena on 1/24/12.
-// Copyright (c) 2009-2012 RestKit. All rights reserved.
-//
-
-#if TARGET_OS_IPHONE
-#import <UIKit/UIKit.h>
-#endif
-
-#import "RKInMemoryEntityCache.h"
-#import "NSManagedObject+ActiveRecord.h"
-#import "../ObjectMapping/RKObjectPropertyInspector.h"
-#import "RKObjectPropertyInspector+CoreData.h"
-#import "RKLog.h"
-
-// Set Logging Component
-#undef RKLogComponent
-#define RKLogComponent lcl_cRestKitCoreData
-
-@interface RKInMemoryEntityCache ()
-@property(nonatomic, retain) NSMutableArray *attributeCaches;
-
-@property(nonatomic, retain) NSMutableDictionary *entityCache;
-
-- (BOOL)shouldCoerceAttributeToString:(NSString *)attribute forEntity:(NSEntityDescription *)entity;
-- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID inContext:(NSManagedObjectContext *)managedObjectContext;
-@end
-
-@implementation RKInMemoryEntityCache
-
-@synthesize entityCache = _entityCache;
-@synthesize managedObjectContext = _managedObjectContext;
-@synthesize attributeCaches = _attributeCaches;
-
-- (id)init {
- self = [super init];
- if (self) {
- _entityCache = [[NSMutableDictionary alloc] init];
-#if TARGET_OS_IPHONE
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(didReceiveMemoryWarning)
- name:UIApplicationDidReceiveMemoryWarningNotification
- object:nil];
-#endif
- }
- return self;
-}
-
-- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
-{
- self = [self init];
- if (self) {
- _managedObjectContext = [managedObjectContext retain];
- }
-
- return self;
-}
-
-- (void)dealloc {
- [[NSNotificationCenter defaultCenter] removeObserver:self];
- [_entityCache release];
- [_managedObjectContext release];
- [_attributeCaches release];
-
- [super dealloc];
-}
-
-- (void)didReceiveMemoryWarning {
- [[NSNotificationCenter defaultCenter] removeObserver:self
- name:NSManagedObjectContextObjectsDidChangeNotification
- object:nil];
- [_entityCache removeAllObjects];
-}
-
-- (NSMutableDictionary *)cachedObjectsForEntity:(NSEntityDescription *)entity
- byAttribute:(NSString *)attributeName
- inContext:(NSManagedObjectContext *)managedObjectContext {
- NSAssert(entity, @"Cannot retrieve cached objects without an entity");
- NSAssert(attributeName, @"Cannot retrieve cached objects without an attributeName");
- NSAssert(managedObjectContext, @"Cannot retrieve cached objects without a managedObjectContext");
-
- NSMutableDictionary *cachedObjectsForEntity = [_entityCache objectForKey:entity.name];
- if (cachedObjectsForEntity == nil) {
- [self cacheObjectsForEntity:entity byAttribute:attributeName inContext:managedObjectContext];
- cachedObjectsForEntity = [_entityCache objectForKey:entity.name];
- }
- return cachedObjectsForEntity;
-}
-
-- (NSManagedObject *)cachedObjectForEntity:(NSEntityDescription *)entity
- withAttribute:(NSString *)attributeName
- value:(id)attributeValue
- inContext:(NSManagedObjectContext *)managedObjectContext {
- NSAssert(entity, @"Cannot retrieve a cached object without an entity");
- NSAssert(attributeName, @"Cannot retrieve a cached object without a mapping");
- NSAssert(attributeValue, @"Cannot retrieve a cached object without a primaryKeyValue");
- NSAssert(managedObjectContext, @"Cannot retrieve a cached object without a managedObjectContext");
-
- NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
- byAttribute:attributeName
- inContext:managedObjectContext];
-
- // NOTE: We coerce the primary key into a string (if possible) for convenience. Generally
- // primary keys are expressed either as a number of a string, so this lets us support either case interchangeably
- id lookupValue = [attributeValue respondsToSelector:@selector(stringValue)] ? [attributeValue stringValue] : attributeValue;
- NSManagedObjectID *objectID = [cachedObjectsForEntity objectForKey:lookupValue];
- NSManagedObject *object = nil;
- if (objectID) {
- object = [self objectWithID:objectID inContext:managedObjectContext];
- }
- return object;
-}
-
-- (void)cacheObjectsForEntity:(NSEntityDescription *)entity
- byAttribute:(NSString *)attributeName
- inContext:(NSManagedObjectContext *)managedObjectContext {
- NSAssert(entity, @"Cannot cache objects without an entity");
- NSAssert(attributeName, @"Cannot cache objects without an attributeName");
- NSAssert(managedObjectContext, @"Cannot cache objects without a managedObjectContext");
-
- NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
- [fetchRequest setEntity:entity];
- [fetchRequest setResultType:NSManagedObjectIDResultType];
-
- NSArray *objectIds = [NSManagedObject executeFetchRequest:fetchRequest inContext:managedObjectContext];
- [fetchRequest release];
-
- RKLogInfo(@"Caching all %ld %@ objectsIDs to thread local storage", (long) [objectIds count], entity.name);
- NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
- if ([objectIds count] > 0) {
- BOOL coerceToString = [self shouldCoerceAttributeToString:attributeName forEntity:entity];
- for (NSManagedObjectID* theObjectID in objectIds) {
- NSManagedObject* theObject = [self objectWithID:theObjectID inContext:managedObjectContext];
- id attributeValue = [theObject valueForKey:attributeName];
- // Coerce to a string if possible
- attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue;
- if (attributeValue) {
- [dictionary setObject:theObjectID forKey:attributeValue];
- }
- }
- }
- [_entityCache setObject:dictionary forKey:entity.name];
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(objectsDidChange:)
- name:NSManagedObjectContextObjectsDidChangeNotification
- object:managedObjectContext];
-}
-
-- (void)cacheObject:(NSManagedObject *)managedObject byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext {
- NSAssert(managedObject, @"Cannot cache an object without a managedObject");
- NSAssert(attributeName, @"Cannot cache an object without a mapping");
- NSAssert(managedObjectContext, @"Cannot cache an object without a managedObjectContext");
-
- NSManagedObjectID *objectID = [managedObject objectID];
- if (objectID) {
- NSEntityDescription *entity = managedObject.entity;
- BOOL coerceToString = [self shouldCoerceAttributeToString:attributeName forEntity:entity];
- id attributeValue = [managedObject valueForKey:attributeName];
- // Coerce to a string if possible
- attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue;
- if (attributeValue) {
- NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
- byAttribute:attributeName
- inContext:managedObjectContext];
- [cachedObjectsForEntity setObject:objectID forKey:attributeValue];
- }
- }
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(objectsDidChange:)
- name:NSManagedObjectContextObjectsDidChangeNotification
- object:managedObjectContext];
-}
-
-- (void)cacheObject:(NSEntityDescription *)entity byAttribute:(NSString *)attributeName value:(id)attributeValue inContext:(NSManagedObjectContext *)managedObjectContext {
- NSAssert(entity, @"Cannot cache an object without an entity");
- NSAssert(attributeName, @"Cannot cache an object without a mapping");
- NSAssert(managedObjectContext, @"Cannot cache an object without a managedObjectContext");
-
- // NOTE: We coerce the primary key into a string (if possible) for convenience. Generally
- // primary keys are expressed either as a number or a string, so this lets us support either case interchangeably
- id lookupValue = [attributeValue respondsToSelector:@selector(stringValue)] ? [attributeValue stringValue] : attributeValue;
-
- NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
- [fetchRequest setEntity:entity];
- [fetchRequest setFetchLimit:1];
- [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", attributeName, lookupValue]];
- [fetchRequest setResultType:NSManagedObjectIDResultType];
-
- NSArray *objectIds = [NSManagedObject executeFetchRequest:fetchRequest inContext:managedObjectContext];
- [fetchRequest release];
-
- NSManagedObjectID *objectID = nil;
- if ([objectIds count] > 0) {
- objectID = [objectIds objectAtIndex:0];
- if (objectID && lookupValue) {
- NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
- byAttribute:attributeName
- inContext:managedObjectContext];
- [cachedObjectsForEntity setObject:objectID forKey:lookupValue];
- }
- }
-
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(objectsDidChange:)
- name:NSManagedObjectContextObjectsDidChangeNotification
- object:managedObjectContext];
-}
-
-- (void)expireCacheEntryForObject:(NSManagedObject *)managedObject byAttribute:(NSString *)attributeName inContext:(NSManagedObjectContext *)managedObjectContext {
- NSAssert(managedObject, @"Cannot expire cache entry for an object without a managedObject");
- NSAssert(attributeName, @"Cannot expire cache entry for an object without a mapping");
- NSAssert(managedObjectContext, @"Cannot expire cache entry for an object without a managedObjectContext");
-
- NSEntityDescription *entity = managedObject.entity;
- BOOL coerceToString = [self shouldCoerceAttributeToString:attributeName forEntity:entity];
- id attributeValue = [managedObject valueForKey:attributeName];
- // Coerce to a string if possible
- attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue;
- if (attributeValue) {
- NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
- byAttribute:attributeName
- inContext:managedObjectContext];
- [cachedObjectsForEntity removeObjectForKey:attributeValue];
-
- if ([cachedObjectsForEntity count] == 0) {
- [self expireCacheEntriesForEntity:entity];
- }
- }
-}
-
-- (void)expireCacheEntriesForEntity:(NSEntityDescription *)entity {
- NSAssert(entity, @"Cannot expire cache entry for an entity without an entity");
- RKLogTrace(@"About to expire cache for entity name=%@", entity.name);
- [_entityCache removeObjectForKey:entity.name];
-}
-
-
-#pragma mark Helper Methods
-
-- (BOOL)shouldCoerceAttributeToString:(NSString *)attribute forEntity:(NSEntityDescription *)entity {
- Class attributeType = [[RKObjectPropertyInspector sharedInspector] typeForProperty:attribute ofEntity:entity];
- return [attributeType instancesRespondToSelector:@selector(stringValue)];
-}
-
-- (NSManagedObject *)objectWithID:(NSManagedObjectID *)objectID inContext:(NSManagedObjectContext *)managedObjectContext {
- NSAssert(objectID, @"Cannot fetch a managedObject with a nil objectID");
- NSAssert(managedObjectContext, @"Cannot fetch a managedObject with a nil managedObjectContext");
- /*
- NOTE:
- We use existingObjectWithID: as opposed to objectWithID: as objectWithID: can return us a fault
- that will raise an exception when fired. existingObjectWithID:error: will return nil if the ID has been
- deleted. objectRegisteredForID: is also an acceptable approach.
- */
- NSError *error = nil;
- NSManagedObject *object = [managedObjectContext existingObjectWithID:objectID error:&error];
- if (! object && error) {
- RKLogError(@"Failed to retrieve managed object with ID %@: %@", objectID, error);
- }
-
- return object;
-}
-
-
-#pragma mark Notifications
-
-- (void)objectsDidChange:(NSNotification *)notification {
- NSDictionary *userInfo = notification.userInfo;
- NSSet *insertedObjects = [userInfo objectForKey:NSInsertedObjectsKey];
- NSSet *deletedObjects = [userInfo objectForKey:NSDeletedObjectsKey];
- RKLogTrace(@"insertedObjects=%@, deletedObjects=%@", insertedObjects, deletedObjects);
-
- NSMutableSet *entitiesToExpire = [NSMutableSet set];
- for (NSManagedObject *object in insertedObjects) {
- [entitiesToExpire addObject:object.entity];
- }
-
- for (NSManagedObject *object in deletedObjects) {
- [entitiesToExpire addObject:object.entity];
- }
-
- for (NSEntityDescription *entity in entitiesToExpire) {
- [self expireCacheEntriesForEntity:entity];
- }
-}
-
-@end
View
1  Code/CoreData/RKInMemoryManagedObjectCache.h
@@ -7,7 +7,6 @@
//
#import "RKManagedObjectCaching.h"
-#import "RKInMemoryEntityCache.h"
/**
Provides a fast managed object cache where-in object instances are retained in
View
60 Code/CoreData/RKInMemoryManagedObjectCache.m
@@ -7,6 +7,8 @@
//
#import "RKInMemoryManagedObjectCache.h"
+#import "NSEntityDescription+RKAdditions.h"
+#import "RKEntityCache.h"
#import "RKLog.h"
// Set Logging Component
@@ -17,27 +19,59 @@
@implementation RKInMemoryManagedObjectCache
-- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
- withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
- value:(id)primaryKeyValue
- inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext {
+- (RKEntityCache *)cacheForEntity:(NSEntityDescription *)entity inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
+{
NSAssert(entity, @"Cannot find existing managed object without a target class");
- NSAssert(primaryKeyAttribute, @"Cannot find existing managed object instance without mapping");
- NSAssert(primaryKeyValue, @"Cannot find existing managed object by primary key without a value");
NSAssert(managedObjectContext, @"Cannot find existing managed object with a context");
NSMutableDictionary *contextDictionary = [[[NSThread currentThread] threadDictionary] objectForKey:RKInMemoryObjectManagedObjectCacheThreadDictionaryKey];
if (! contextDictionary) {
contextDictionary = [NSMutableDictionary dictionaryWithCapacity:1];
[[[NSThread currentThread] threadDictionary] setObject:contextDictionary forKey:RKInMemoryObjectManagedObjectCacheThreadDictionaryKey];
}
- NSNumber *hashNumber = [NSNumber numberWithUnsignedInteger:[managedObjectContext hash]];
- RKInMemoryEntityCache *cache = [contextDictionary objectForKey:hashNumber];
- if (! cache) {
- cache = [[RKInMemoryEntityCache alloc] initWithManagedObjectContext:managedObjectContext];
- [contextDictionary setObject:cache forKey:hashNumber];
- [cache release];
+ NSNumber *hashNumber = [NSNumber numberWithUnsignedInteger:[managedObjectContext hash]];
+ RKEntityCache *entityCache = [contextDictionary objectForKey:hashNumber];
+ if (! entityCache) {
+ RKLogInfo(@"Creating thread-local entity cache for managed object context: %@", managedObjectContext);
+ entityCache = [[RKEntityCache alloc] initWithManagedObjectContext:managedObjectContext];
+ [contextDictionary setObject:entityCache forKey:hashNumber];
+ [entityCache release];
}
- return [cache cachedObjectForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue inContext:managedObjectContext];
+
+ return entityCache;
+}
+
+- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
+ withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
+ value:(id)primaryKeyValue
+ inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
+{
+ RKEntityCache *entityCache = [self cacheForEntity:entity inManagedObjectContext:managedObjectContext];
+ if (! [entityCache isEntity:entity cachedByAttribute:primaryKeyAttribute]) {
+ RKLogInfo(@"Caching instances of Entity '%@' by primary key attribute '%@'", entity.name, primaryKeyAttribute);
+ [entityCache cacheObjectsForEntity:entity byAttribute:primaryKeyAttribute];
+ RKEntityByAttributeCache *attributeCache = [entityCache attributeCacheForEntity:entity attribute:primaryKeyAttribute];
+ RKLogTrace(@"Cached %ld objects", (long) [attributeCache count]);
+ }
+
+ return [entityCache objectForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue];
+}
+
+- (void)didFetchObject:(NSManagedObject *)object
+{
+ RKEntityCache *entityCache = [self cacheForEntity:object.entity inManagedObjectContext:object.managedObjectContext];
+ [entityCache addObject:object];
+}
+
+- (void)didCreateObject:(NSManagedObject *)object
+{
+ RKEntityCache *entityCache = [self cacheForEntity:object.entity inManagedObjectContext:object.managedObjectContext];
+ [entityCache addObject:object];
+}
+
+- (void)didDeleteObject:(NSManagedObject *)object
+{
+ RKEntityCache *entityCache = [self cacheForEntity:object.entity inManagedObjectContext:object.managedObjectContext];
+ [entityCache removeObject:object];
}
@end
View
25 Code/CoreData/RKManagedObjectCaching.h
@@ -16,6 +16,8 @@
*/
@protocol RKManagedObjectCaching
+@required
+
/**
Retrieves a model object from the object store given a Core Data entity and
the primary key attribute and value for the desired object.
@@ -32,4 +34,27 @@
value:(id)primaryKeyValue
inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
+@optional
+
+/**
+ Tells the receiver that an object was fetched and should be added to the cache.
+
+ @param object The object that was fetched from a managed object context.
+ */
+- (void)didFetchObject:(NSManagedObject *)object;
+
+/**
+ Tells the receiver that an object was created and should be added to the cache.
+
+ @param object The object that was created in a managed object context.
+ */
+- (void)didCreateObject:(NSManagedObject *)object;
+
+/**
+ Tells the receiver that an object was deleted and should be removed to the cache.
+
+ @param object The object that was deleted from a managed object context.
+ */
+- (void)didDeleteObject:(NSManagedObject *)object;
+
@end
View
21 Code/CoreData/RKManagedObjectMapping.m
@@ -160,19 +160,34 @@ - (id)mappableObjectForData:(id)mappableData {
NSString* keyPathForPrimaryKeyElement = primaryKeyAttributeMapping.sourceKeyPath;
if (keyPathForPrimaryKeyElement) {
primaryKeyValue = [mappableData valueForKeyPath:keyPathForPrimaryKeyElement];
+ } else {
+ RKLogWarning(@"Unable to find source attribute for primaryKeyAttribute '%@': unable to find existing object instances by primary key.", primaryKeyAttribute);
}
}
}
// If we have found the primary key attribute & value, try to find an existing instance to update
- if (primaryKeyAttribute && primaryKeyValue) {
- object = [self.objectStore.cacheStrategy findInstanceOfEntity:entity
- withPrimaryKeyAttribute:self.primaryKeyAttribute value:primaryKeyValue inManagedObjectContext:[self.objectStore managedObjectContextForCurrentThread]];
+ if (primaryKeyAttribute && primaryKeyValue && NO == [primaryKeyValue isEqual:[NSNull null]]) {
+ object = [self.objectStore.cacheStrategy findInstanceOfEntity:entity
+ withPrimaryKeyAttribute:primaryKeyAttribute
+ value:primaryKeyValue
+ inManagedObjectContext:[self.objectStore managedObjectContextForCurrentThread]];
+
+ if (object && [self.objectStore.cacheStrategy respondsToSelector:@selector(didFetchObject:)]) {
+ [self.objectStore.cacheStrategy didFetchObject:object];
+ }
}
if (object == nil) {
object = [[[NSManagedObject alloc] initWithEntity:entity
insertIntoManagedObjectContext:[_objectStore managedObjectContextForCurrentThread]] autorelease];
+ if (primaryKeyAttribute && primaryKeyValue && ![primaryKeyValue isEqual:[NSNull null]]) {
+ [object setValue:primaryKeyValue forKey:primaryKeyAttribute];
+ }
+
+ if ([self.objectStore.cacheStrategy respondsToSelector:@selector(didCreateObject:)]) {
+ [self.objectStore.cacheStrategy didCreateObject:object];
+ }
}
return object;
}
View
2  Code/CoreData/RKManagedObjectMappingOperation.m
@@ -119,7 +119,7 @@ - (BOOL)performMapping:(NSError **)error {
manually invoke processPendingChanges to prevent recreating objects with the same primary key.
See https://github.com/RestKit/RestKit/issues/661
*/
- [[[(RKManagedObjectMapping *)self.objectMapping objectStore] managedObjectContextForCurrentThread] processPendingChanges];
+// [[[(RKManagedObjectMapping *)self.objectMapping objectStore] managedObjectContextForCurrentThread] processPendingChanges];
[self connectRelationships];
}
return success;
View
5 Code/CoreData/RKManagedObjectStore.m
@@ -126,8 +126,7 @@ - (id)initWithStoreFilename:(NSString *)storeFilename inDirectory:(NSString *)ni
}
NSMutableArray* allManagedObjectModels = [NSMutableArray arrayWithObject:nilOrManagedObjectModel];
_managedObjectModel = [[NSManagedObjectModel modelByMergingModels:allManagedObjectModels] retain];
-
- _delegate = delegate;
+ _delegate = delegate;
if (nilOrNameOfSeedDatabaseInMainBundle) {
[self createStoreIfNecessaryUsingSeedDatabase:nilOrNameOfSeedDatabaseInMainBundle];
@@ -136,7 +135,7 @@ - (id)initWithStoreFilename:(NSString *)storeFilename inDirectory:(NSString *)ni
[self createPersistentStoreCoordinator];
self.primaryManagedObjectContext = [[self newManagedObjectContext] autorelease];
- _cacheStrategy = [RKFetchRequestManagedObjectCache new];
+ _cacheStrategy = [RKInMemoryManagedObjectCache new];
// Ensure there is a search word observer
[RKSearchWordObserver sharedObserver];
View
121 Code/Support/RKBenchmark.h
@@ -0,0 +1,121 @@
+//
+// RKBenchmark.h
+// RestKit
+//
+// Derived from Benchmark class: https://gist.github.com/1479490
+// Created by Sijawusz Pur Rahnama on 03/02/09.
+// Copyleft 2009. Some rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+/**
+ RKBenchmark objects provide a simple, lightweight interface for
+ quickly benchmarking the performance of units of code. Benchmark
+ objects can be used procedurally, by manually starting & stopping
+ the benchmark, or using a block interface to measure the execution
+ time of the block.
+ */
+@interface RKBenchmark : NSObject
+
+///-----------------------------------------------------------------------------
+/// @name Accessing Benchmark Values
+///-----------------------------------------------------------------------------
+
+/**
+ A name for the benchmark. Can be nil.
+ */
+@property (nonatomic, retain) NSString *name;
+
+/**
+ The start time of the benchmark as an absolute time value.
+ */
+@property (nonatomic, assign, readonly) CFAbsoluteTime startTime;
+
+/**
+ The end time of the benchmark as an absolute time value.
+ */
+@property (nonatomic, assign, readonly) CFAbsoluteTime endTime;
+
+/**
+ The elapsed time of the benchmark as determined by subtracting the
+ end time from the start time. Returns zero until the benchmark has
+ been stopped.
+ */
+@property (nonatomic, assign, readonly) CFTimeInterval elapsedTime;
+
+///-----------------------------------------------------------------------------
+/// @name Quickly Performing Benchmarks
+///-----------------------------------------------------------------------------
+
+/**
+ */
++ (id)report:(NSString *)info executionBlock:(void (^)(void))block;
+
+/**
+ Performs a benchmark and returns a time interval measurement of the
+ total time elapsed during the execution of the blocl.
+
+ @param block A block to execute and measure the elapsed time during execution.
+ @return A time interval equal to the total time elapsed during execution.
+ */
++ (CFTimeInterval)measureWithExecutionBlock:(void (^)(void))block;
+
+///-----------------------------------------------------------------------------
+/// @name Creating Benchmark Objects
+///-----------------------------------------------------------------------------
+
+/**
+ Retrieves or creates a benchmark object instance with a given name.
+
+ @param name A name for the benchmark.
+ @return A new or existing benchmark object with the given name.
+ */
++ (RKBenchmark *)instanceWithName:(NSString *)name;
+
+/**
+ Creates and returns a benchmark object with a name.
+
+ @param name A name for the benchmark.
+ @return A new benchmark object with the given name.
+ */
++ (id)benchmarkWithName:(NSString *)name;
+
+/**
+ Initializes a new benchmark object with a name.
+
+ @param name The name to initialize the receiver with.
+ @return The receiver, initialized with the given name.
+ */
+- (id)initWithName:(NSString *)name;
+
+///-----------------------------------------------------------------------------
+/// @name Performing Benchmarks
+///-----------------------------------------------------------------------------
+
+/**
+ Runs a benchmark by starting the receiver, executing the block, and then stopping
+ the benchmark object.
+
+ @param executionBlock A block to execute as the body of the benchmark.
+ */
+- (void)run:(void (^)(void))executionBlock;
+
+/**
+ Starts the benchmark by recording the start time.
+ */
+- (void)start;
+
+/**
+ Stops the benchmark by recording the stop time.
+ */
+- (void)stop;
+
+/**
+ Logs the current benchmark status. If the receiver has been stopped, the
+ elapsed time of the benchmark is logged. If the benchmark is still running,
+ the total time since the benchmark was started is logged.
+ */
+- (void)log;
+
+@end
View
119 Code/Support/RKBenchmark.m
@@ -0,0 +1,119 @@
+//
+// RKBenchmark.h
+// RestKit
+//
+// Derived from Benchmark class: https://gist.github.com/1479490
+// Created by Sijawusz Pur Rahnama on 03/02/09.
+// Copyleft 2009. Some rights reserved.
+//
+
+#import "RKBenchmark.h"
+
+@interface RKBenchmark ()
+@property (nonatomic, assign, readwrite) CFAbsoluteTime startTime;
+@property (nonatomic, assign, readwrite) CFAbsoluteTime endTime;
+@property (nonatomic, assign, readwrite) CFTimeInterval elapsedTime;
+@property (nonatomic, assign, getter = isStopped) BOOL stopped;
+@end
+
+@implementation RKBenchmark
+
+static NSMutableDictionary * __sharedBenchmarks = nil;
+
++ (NSMutableDictionary *)sharedBenchmarks {
+ if (!__sharedBenchmarks) {
+ __sharedBenchmarks = [[NSMutableDictionary alloc] init];
+ }
+ return __sharedBenchmarks;
+}
+
++ (id)instanceWithName:(NSString *)name {
+ @synchronized (self) {
+ // get the benchmark or create it on-the-fly
+ id benchmark = [[self sharedBenchmarks] objectForKey:name];
+ if (!benchmark) {
+ benchmark = [self benchmarkWithName:name];
+ [[self sharedBenchmarks] setObject:benchmark forKey:name];
+ }
+ return benchmark;
+ }
+ return nil;
+}
+
+@synthesize name = _name;
+@synthesize startTime = _startTime;
+@synthesize endTime = _endTime;
+@synthesize elapsedTime = _elapsedTime;
+@synthesize stopped = _stopped;
+
+# pragma mark -
+# pragma mark Quick access class methods
+
++ (id)report:(NSString *)info executionBlock:(void (^)(void))block {
+ RKBenchmark *benchmark = [self instanceWithName:info];
+ [benchmark run:block];
+ [benchmark log];
+ return benchmark;
+}
+
++ (CFTimeInterval)measureWithExecutionBlock:(void (^)(void))block {
+ RKBenchmark *benchmark = [[self new] autorelease];
+ [benchmark run:block];
+ return benchmark.elapsedTime;
+}
+
+# pragma mark -
+# pragma mark Initializers
+
++ (id)benchmarkWithName:(NSString *)name {
+ return [[self alloc] initWithName:name];
+}
+
+- (id)initWithName:(NSString *)name {
+ if (self = [self init]) {
+ self.name = name;
+ }
+ return self;
+}
+
+# pragma mark -
+# pragma mark Benchmark methods
+
+- (void)run:(void (^)(void))executionBlock {
+ [self start];
+ executionBlock();
+ [self stop];
+}
+
+- (void)start {
+ self.startTime = CFAbsoluteTimeGetCurrent();
+}
+
+- (void)stop {
+ self.endTime = CFAbsoluteTimeGetCurrent();
+ self.stopped = YES;
+
+ // Calculate elapsed time
+ CFDateRef startDate = CFDateCreate(NULL, self.startTime);
+ CFDateRef endDate = CFDateCreate(NULL, self.endTime);
+ self.elapsedTime = CFDateGetTimeIntervalSinceDate(endDate, startDate);
+ CFRelease(startDate);
+ CFRelease(endDate);
+}
+
+- (void)log {
+ CFTimeInterval timeElapsed;
+ if (self.isStopped) {
+ timeElapsed = self.elapsedTime;
+ } else {
+ CFDateRef startDate = CFDateCreate(NULL, self.startTime);
+ timeElapsed = CFDateGetTimeIntervalSinceDate(startDate, (CFDateRef)[NSDate date]);
+ CFRelease(startDate);
+ }
+
+ // log elapsed time
+ if (_name) NSLog(@"Benchmark '%@' took %f seconds.", _name, timeElapsed);
+ else NSLog(@"Benchmark took %f seconds.", timeElapsed);
+}
+
+@end
View
1  Code/Support/Support.h
@@ -25,6 +25,7 @@
#import "RKPathMatcher.h"
#import "RKDotNetDateFormatter.h"
#import "RKDirectory.h"
+#import "RKBenchmark.h"
// Load our categories
#import "NSDictionary+RKAdditions.h"
View
1  Code/Support/lcl_config_components.h
@@ -56,6 +56,7 @@ _lcl_component(RestKitNetworkQueue, "restkit.network.queue",
_lcl_component(RestKitNetworkReachability, "restkit.network.reachability", "RestKit/Network/Reachability")
_lcl_component(RestKitObjectMapping, "restkit.object_mapping", "RestKit/ObjectMapping")
_lcl_component(RestKitCoreData, "restkit.core_data", "RestKit/CoreData")
+_lcl_component(RestKitCoreDataCache, "restkit.core_data.cache", "RestKit/CoreData/Cache")
_lcl_component(RestKitCoreDataSearchEngine, "restkit.core_data.search_engine", "RestKit/CoreData/SearchEngine")
_lcl_component(RestKitSupport, "restkit.support", "RestKit/Support")
_lcl_component(RestKitSupportParsers, "restkit.support.parsers", "RestKit/Support/Parsers")
View
5 Gemfile
@@ -7,7 +7,7 @@ gem "thin", "~> 1.3.1"
gem 'xcoder', :git => "git://github.com/rayh/xcoder.git"
gem 'restkit', :git => 'git://github.com/RestKit/RestKit-Gem.git'
gem 'ruby-debug19'
-
+gem 'faker', '1.0.1'
## OAuth stuff
@@ -16,4 +16,5 @@ gem 'rack-oauth2-server', :git => 'https://github.com/assaf/rack-oauth2-server.g
gem 'rspec'
gem 'rack-test'
gem 'mongo'
-gem 'simple_oauth', :git => 'https://github.com/laserlemon/simple_oauth.git'
+gem 'simple_oauth', :git => 'https://github.com/laserlemon/simple_oauth.git'
+
View
12 Gemfile.lock
@@ -17,7 +17,7 @@ GIT
GIT
remote: git://github.com/sinatra/sinatra.git
- revision: 5a8080a077c298efc9917f7126503e99b9adb60b
+ revision: cab56ed7d64b9ffb6d4df48cb8d7d3153890f3ee
specs:
sinatra (1.4.0)
rack (~> 1.3, >= 1.3.6)
@@ -38,9 +38,9 @@ GIT
GIT
remote: https://github.com/laserlemon/simple_oauth.git
- revision: f7124aba147418ea198c88b6a7c4e3cd0c3fe4a9
+ revision: e1146b615df769bfe7824a337122069cc2d5476d
specs:
- simple_oauth (0.1.7)
+ simple_oauth (0.1.8)
GEM
remote: http://rubygems.org/
@@ -54,7 +54,10 @@ GEM
daemons (1.1.8)
diff-lcs (1.1.3)
eventmachine (0.12.10)
- json (1.7.1)
+ faker (1.0.1)
+ i18n (~> 0.4)
+ i18n (0.6.0)
+ json (1.7.3)
jwt (0.1.4)
json (>= 1.2.4)
linecache19 (0.5.12)
@@ -101,6 +104,7 @@ PLATFORMS
DEPENDENCIES
bundler (~> 1.1.0)
+ faker (= 1.0.1)
mongo
rack-oauth2-server!
rack-test
View
22 Rakefile
@@ -198,3 +198,25 @@ desc "Validate a branch is ready for merging by checking for common issues"
task :validate => [:build, 'docs:check', 'uispec:all'] do
puts "Project state validated successfully. Proceed with merge."
end
+
+namespace :payload do
+ task :generate do
+ require 'json'
+ require 'faker'
+
+ ids = (1..25).to_a
+ child_ids = (50..100).to_a
+ child_counts = (10..25).to_a
+ hash = ids.inject({'parents' => []}) do |hash, parent_id|
+ child_count = child_counts.sample
+ children = (0..child_count).collect do
+ {'name' => Faker::Name.name, 'childID' => child_ids.sample}
+ end
+ parent = {'parentID' => parent_id, 'name' => Faker::Name.name, 'children' => children}
+ hash['parents'] << parent
+ hash
+ end
+ File.open('payload.json', 'w+') { |f| f << hash.to_json }
+ puts "Generated payload at: payload.json"
+ end
+end
View
78 RestKit.xcodeproj/project.pbxproj
@@ -38,6 +38,8 @@
250DF22D14C5190E0001DEFA /* RKOrderedDictionary.m in Sources */ = {isa = PBXBuildFile; fileRef = 250DF22914C5190E0001DEFA /* RKOrderedDictionary.m */; };
250DF25F14C680F90001DEFA /* RKObjectMappingProvider+Contexts.h in Headers */ = {isa = PBXBuildFile; fileRef = 250DF25E14C680F90001DEFA /* RKObjectMappingProvider+Contexts.h */; settings = {ATTRIBUTES = (Public, ); }; };
250DF26014C680F90001DEFA /* RKObjectMappingProvider+Contexts.h in Headers */ = {isa = PBXBuildFile; fileRef = 250DF25E14C680F90001DEFA /* RKObjectMappingProvider+Contexts.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 25119FB6154A34B400C6BC58 /* parents_and_children.json in Resources */ = {isa = PBXBuildFile; fileRef = 25119FB5154A34B400C6BC58 /* parents_and_children.json */; };
+ 25119FB7154A34B400C6BC58 /* parents_and_children.json in Resources */ = {isa = PBXBuildFile; fileRef = 25119FB5154A34B400C6BC58 /* parents_and_children.json */; };
2513504E14B8FE6B00A7E893 /* RKConfigurationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2513504D14B8FE6B00A7E893 /* RKConfigurationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
2513504F14B8FE6B00A7E893 /* RKConfigurationDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 2513504D14B8FE6B00A7E893 /* RKConfigurationDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
25160D1A14564E810060A5C5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25160D1914564E810060A5C5 /* Foundation.framework */; };
@@ -508,6 +510,10 @@
2572538E155C543000CB05ED /* RKPortCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 2572538B155C543000CB05ED /* RKPortCheck.h */; settings = {ATTRIBUTES = (Public, ); }; };
2572538F155C543000CB05ED /* RKPortCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 2572538C155C543000CB05ED /* RKPortCheck.m */; };
25725390155C543000CB05ED /* RKPortCheck.m in Sources */ = {isa = PBXBuildFile; fileRef = 2572538C155C543000CB05ED /* RKPortCheck.m */; };
+ 25545959155F0527007D7625 /* RKBenchmark.h in Headers */ = {isa = PBXBuildFile; fileRef = 25545957155F0527007D7625 /* RKBenchmark.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 2554595A155F0527007D7625 /* RKBenchmark.h in Headers */ = {isa = PBXBuildFile; fileRef = 25545957155F0527007D7625 /* RKBenchmark.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 2554595B155F0527007D7625 /* RKBenchmark.m in Sources */ = {isa = PBXBuildFile; fileRef = 25545958155F0527007D7625 /* RKBenchmark.m */; };
+ 2554595C155F0527007D7625 /* RKBenchmark.m in Sources */ = {isa = PBXBuildFile; fileRef = 25545958155F0527007D7625 /* RKBenchmark.m */; };
257ABAB015112DD500CCAA76 /* NSManagedObjectContext+RKAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 257ABAAE15112DD400CCAA76 /* NSManagedObjectContext+RKAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
257ABAB115112DD500CCAA76 /* NSManagedObjectContext+RKAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 257ABAAE15112DD400CCAA76 /* NSManagedObjectContext+RKAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
257ABAB215112DD500CCAA76 /* NSManagedObjectContext+RKAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 257ABAAF15112DD400CCAA76 /* NSManagedObjectContext+RKAdditions.m */; };
@@ -525,6 +531,20 @@
259C3027151280A1003066A2 /* grayArrow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 25EC1AE314F8022600C3CF3F /* grayArrow@2x.png */; };
259C3028151280A1003066A2 /* whiteArrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 25EC1AE414F8022600C3CF3F /* whiteArrow.png */; };
259C3029151280A1003066A2 /* whiteArrow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 25EC1AE514F8022600C3CF3F /* whiteArrow@2x.png */; };
+ 259D983C154F6C90008C90F5 /* benchmark_parents_and_children.json in Resources */ = {isa = PBXBuildFile; fileRef = 259D983B154F6C90008C90F5 /* benchmark_parents_and_children.json */; };
+ 259D983D154F6C90008C90F5 /* benchmark_parents_and_children.json in Resources */ = {isa = PBXBuildFile; fileRef = 259D983B154F6C90008C90F5 /* benchmark_parents_and_children.json */; };
+ 259D98541550C69A008C90F5 /* RKEntityByAttributeCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 259D98521550C69A008C90F5 /* RKEntityByAttributeCache.h */; };
+ 259D98551550C69A008C90F5 /* RKEntityByAttributeCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 259D98521550C69A008C90F5 /* RKEntityByAttributeCache.h */; };
+ 259D98561550C69A008C90F5 /* RKEntityByAttributeCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D98531550C69A008C90F5 /* RKEntityByAttributeCache.m */; };
+ 259D98571550C69A008C90F5 /* RKEntityByAttributeCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D98531550C69A008C90F5 /* RKEntityByAttributeCache.m */; };
+ 259D985A1550C6BE008C90F5 /* RKEntityByAttributeCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D98591550C6BE008C90F5 /* RKEntityByAttributeCacheTest.m */; };
+ 259D985B1550C6BE008C90F5 /* RKEntityByAttributeCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D98591550C6BE008C90F5 /* RKEntityByAttributeCacheTest.m */; };
+ 259D985E155218E5008C90F5 /* RKEntityCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 259D985C155218E4008C90F5 /* RKEntityCache.h */; };
+ 259D985F155218E5008C90F5 /* RKEntityCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 259D985C155218E4008C90F5 /* RKEntityCache.h */; };
+ 259D9860155218E5008C90F5 /* RKEntityCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D985D155218E4008C90F5 /* RKEntityCache.m */; };
+ 259D9861155218E5008C90F5 /* RKEntityCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D985D155218E4008C90F5 /* RKEntityCache.m */; };
+ 259D986415521B20008C90F5 /* RKEntityCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D986315521B1F008C90F5 /* RKEntityCacheTest.m */; };
+ 259D986515521B20008C90F5 /* RKEntityCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 259D986315521B1F008C90F5 /* RKEntityCacheTest.m */; };
25A2476E153E667E003240B6 /* RKCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A2476D153E667E003240B6 /* RKCacheTest.m */; };
25A2476F153E667E003240B6 /* RKCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25A2476D153E667E003240B6 /* RKCacheTest.m */; };
25A34245147D8AAA0009758D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 25A34244147D8AAA0009758D /* Security.framework */; };
@@ -659,10 +679,6 @@
25EC1A2D14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.h in Headers */ = {isa = PBXBuildFile; fileRef = 25EC1A2A14F6FDAC00C3CF3F /* RKObjectManager+RKTableController.h */; settings = {ATTRIBUTES = (Public, ); }; };
25EC1A2E14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.m in Sources */ = {isa = PBXBuildFile; fileRef = 25EC1A2B14F6FDAC00C3CF3F /* RKObjectManager+RKTableController.m */; };
25EC1A2F14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.m in Sources */ = {isa = PBXBuildFile; fileRef = 25EC1A2B14F6FDAC00C3CF3F /* RKObjectManager+RKTableController.m */; };
- 25EC1A3414F72AF000C3CF3F /* RKInMemoryEntityCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF4114CF1BB200CE7BCE /* RKInMemoryEntityCache.m */; };
- 25EC1A3614F72AF100C3CF3F /* RKInMemoryEntityCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF4114CF1BB200CE7BCE /* RKInMemoryEntityCache.m */; };
- 25EC1A3714F72B0100C3CF3F /* RKInMemoryEntityCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF4014CF1BB200CE7BCE /* RKInMemoryEntityCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 25EC1A3814F72B0200C3CF3F /* RKInMemoryEntityCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF4014CF1BB200CE7BCE /* RKInMemoryEntityCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
25EC1A3914F72B0900C3CF3F /* RKFetchRequestManagedObjectCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3814CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
25EC1A3A14F72B0A00C3CF3F /* RKFetchRequestManagedObjectCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3814CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
25EC1A3B14F72B1300C3CF3F /* RKFetchRequestManagedObjectCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7394DF3914CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.m */; };
@@ -678,8 +694,6 @@
25EC1A4614F7393E00C3CF3F /* RKObjectMappingProviderContextEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = 73DA8E2114D1C3870054DD73 /* RKObjectMappingProviderContextEntry.m */; };
25EC1A4714F7394100C3CF3F /* RKObjectMappingProviderContextEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 73DA8E2014D1C3870054DD73 /* RKObjectMappingProviderContextEntry.h */; settings = {ATTRIBUTES = (Public, ); }; };
25EC1A4814F7394200C3CF3F /* RKObjectMappingProviderContextEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = 73DA8E2014D1C3870054DD73 /* RKObjectMappingProviderContextEntry.h */; settings = {ATTRIBUTES = (Public, ); }; };
- 25EC1A4A14F73CA200C3CF3F /* RKInMemoryEntityCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25EC1A4914F73CA200C3CF3F /* RKInMemoryEntityCacheTest.m */; };
- 25EC1A4B14F73CA200C3CF3F /* RKInMemoryEntityCacheTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 25EC1A4914F73CA200C3CF3F /* RKInMemoryEntityCacheTest.m */; };
25EC1A6314F7402A00C3CF3F /* RKManagedObjectCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3514CF157A00CE7BCE /* RKManagedObjectCaching.h */; settings = {ATTRIBUTES = (Public, ); }; };
25EC1A6514F7402A00C3CF3F /* RKManagedObjectCaching.h in Headers */ = {isa = PBXBuildFile; fileRef = 7394DF3514CF157A00CE7BCE /* RKManagedObjectCaching.h */; settings = {ATTRIBUTES = (Public, ); }; };
25EC1ABC14F8019F00C3CF3F /* RKRefreshGestureRecognizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 25EC1AB814F8019F00C3CF3F /* RKRefreshGestureRecognizer.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -772,6 +786,7 @@
250DF22814C5190E0001DEFA /* RKOrderedDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKOrderedDictionary.h; sourceTree = "<group>"; };
250DF22914C5190E0001DEFA /* RKOrderedDictionary.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKOrderedDictionary.m; sourceTree = "<group>"; };
250DF25E14C680F90001DEFA /* RKObjectMappingProvider+Contexts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RKObjectMappingProvider+Contexts.h"; sourceTree = "<group>"; };
+ 25119FB5154A34B400C6BC58 /* parents_and_children.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = parents_and_children.json; sourceTree = "<group>"; };
2513504D14B8FE6B00A7E893 /* RKConfigurationDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKConfigurationDelegate.h; sourceTree = "<group>"; };
25160D1614564E810060A5C5 /* libRestKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRestKit.a; sourceTree = BUILT_PRODUCTS_DIR; };
25160D1914564E810060A5C5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -1045,11 +1060,20 @@
254A62BF14AD591C00939BEE /* RKObjectPaginatorTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKObjectPaginatorTest.m; sourceTree = "<group>"; };
2572538B155C543000CB05ED /* RKPortCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKPortCheck.h; sourceTree = "<group>"; };
2572538C155C543000CB05ED /* RKPortCheck.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKPortCheck.m; sourceTree = "<group>"; };
+ 25545957155F0527007D7625 /* RKBenchmark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKBenchmark.h; sourceTree = "<group>"; };
+ 25545958155F0527007D7625 /* RKBenchmark.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKBenchmark.m; sourceTree = "<group>"; };
257ABAAE15112DD400CCAA76 /* NSManagedObjectContext+RKAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObjectContext+RKAdditions.h"; sourceTree = "<group>"; };
257ABAAF15112DD400CCAA76 /* NSManagedObjectContext+RKAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObjectContext+RKAdditions.m"; sourceTree = "<group>"; };
257ABAB41511371C00CCAA76 /* NSManagedObject+RKAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSManagedObject+RKAdditions.h"; sourceTree = "<group>"; };
257ABAB51511371D00CCAA76 /* NSManagedObject+RKAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSManagedObject+RKAdditions.m"; sourceTree = "<group>"; };
259C301615128079003066A2 /* RestKitResources.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RestKitResources.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
+ 259D983B154F6C90008C90F5 /* benchmark_parents_and_children.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = benchmark_parents_and_children.json; sourceTree = "<group>"; };
+ 259D98521550C69A008C90F5 /* RKEntityByAttributeCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKEntityByAttributeCache.h; sourceTree = "<group>"; };
+ 259D98531550C69A008C90F5 /* RKEntityByAttributeCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKEntityByAttributeCache.m; sourceTree = "<group>"; };
+ 259D98591550C6BE008C90F5 /* RKEntityByAttributeCacheTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKEntityByAttributeCacheTest.m; sourceTree = "<group>"; };
+ 259D985C155218E4008C90F5 /* RKEntityCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKEntityCache.h; sourceTree = "<group>"; };
+ 259D985D155218E4008C90F5 /* RKEntityCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKEntityCache.m; sourceTree = "<group>"; };
+ 259D986315521B1F008C90F5 /* RKEntityCacheTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKEntityCacheTest.m; sourceTree = "<group>"; };
25A2476D153E667E003240B6 /* RKCacheTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKCacheTest.m; sourceTree = "<group>"; };
25A34244147D8AAA0009758D /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
25B408241491CDDB00F21111 /* RKDirectory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKDirectory.h; sourceTree = "<group>"; };
@@ -1151,7 +1175,6 @@
25E36E0115195CED00F9E448 /* RKFetchRequestMappingCacheTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKFetchRequestMappingCacheTest.m; sourceTree = "<group>"; };
25EC1A2A14F6FDAC00C3CF3F /* RKObjectManager+RKTableController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RKObjectManager+RKTableController.h"; sourceTree = "<group>"; };
25EC1A2B14F6FDAC00C3CF3F /* RKObjectManager+RKTableController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RKObjectManager+RKTableController.m"; sourceTree = "<group>"; };
- 25EC1A4914F73CA200C3CF3F /* RKInMemoryEntityCacheTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKInMemoryEntityCacheTest.m; sourceTree = "<group>"; };
25EC1AB814F8019F00C3CF3F /* RKRefreshGestureRecognizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKRefreshGestureRecognizer.h; sourceTree = "<group>"; };
25EC1AB914F8019F00C3CF3F /* RKRefreshGestureRecognizer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKRefreshGestureRecognizer.m; sourceTree = "<group>"; };
25EC1ABA14F8019F00C3CF3F /* RKRefreshTriggerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKRefreshTriggerView.h; sourceTree = "<group>"; };
@@ -1192,8 +1215,6 @@
7394DF3914CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKFetchRequestManagedObjectCache.m; sourceTree = "<group>"; };
7394DF3C14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKInMemoryManagedObjectCache.h; sourceTree = "<group>"; };
7394DF3D14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKInMemoryManagedObjectCache.m; sourceTree = "<group>"; };
- 7394DF4014CF1BB200CE7BCE /* RKInMemoryEntityCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RKInMemoryEntityCache.h; sourceTree = "<group>"; };
- 7394DF4114CF1BB200CE7BCE /* RKInMemoryEntityCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RKInMemoryEntityCache.m; sourceTree = "<group>"; };
73D3907114CA19F90093E3D6 /* parent.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = parent.json; sourceTree = "<group>"; };
73D3907314CA1A4A0093E3D6 /* child.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = child.json; sourceTree = "<group>"; };
73D3907814CA1D710093E3D6 /* channels.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = channels.xml; sourceTree = "<group>"; };
@@ -1354,8 +1375,6 @@
25160D48145650490060A5C5 /* NSManagedObject+ActiveRecord.m */,
7394DF3814CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.h */,
7394DF3914CF168C00CE7BCE /* RKFetchRequestManagedObjectCache.m */,
- 7394DF4014CF1BB200CE7BCE /* RKInMemoryEntityCache.h */,
- 7394DF4114CF1BB200CE7BCE /* RKInMemoryEntityCache.m */,
7394DF3C14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.h */,
7394DF3D14CF19F200CE7BCE /* RKInMemoryManagedObjectCache.m */,
25160D4A145650490060A5C5 /* RKManagedObjectLoader.h */,
@@ -1381,6 +1400,10 @@
257ABAB51511371D00CCAA76 /* NSManagedObject+RKAdditions.m */,
25079C6D151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h */,
25079C6E151B93DB00266AE7 /* NSEntityDescription+RKAdditions.m */,
+ 259D98521550C69A008C90F5 /* RKEntityByAttributeCache.h */,
+ 259D98531550C69A008C90F5 /* RKEntityByAttributeCache.m */,
+ 259D985C155218E4008C90F5 /* RKEntityCache.h */,
+ 259D985D155218E4008C90F5 /* RKEntityCache.m */,
);
path = CoreData;
sourceTree = "<group>";
@@ -1478,6 +1501,8 @@
25160DA2145650490060A5C5 /* Support */ = {
isa = PBXGroup;
children = (
+ 25545957155F0527007D7625 /* RKBenchmark.h */,
+ 25545958155F0527007D7625 /* RKBenchmark.m */,
25B6E9F914CF943D00B1E881 /* RKCache.h */,
25B6E9FA14CF943E00B1E881 /* RKCache.m */,
25B6E9FB14CF943E00B1E881 /* RKMutableBlockDictionary.h */,
@@ -1676,7 +1701,6 @@
25160FC61456F2330060A5C5 /* CoreData */ = {
isa = PBXGroup;
children = (
- 25EC1A4914F73CA200C3CF3F /* RKInMemoryEntityCacheTest.m */,
252EFB0614D98F4D004863C8 /* RKSearchableManagedObjectTest.m */,
252EFB0714D98F4D004863C8 /* RKSearchWordObserverTest.m */,
25160FC71456F2330060A5C5 /* RKManagedObjectLoaderTest.m */,
@@ -1687,6 +1711,8 @@
25E36E0115195CED00F9E448 /* RKFetchRequestMappingCacheTest.m */,
25079C75151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m */,
25DB7507151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m */,
+ 259D98591550C6BE008C90F5 /* RKEntityByAttributeCacheTest.m */,
+ 259D986315521B1F008C90F5 /* RKEntityCacheTest.m */,
);
name = CoreData;
path = Logic/CoreData;
@@ -1714,6 +1740,7 @@
25160FD01456F2330060A5C5 /* JSON */ = {
isa = PBXGroup;
children = (
+ 259D983B154F6C90008C90F5 /* benchmark_parents_and_children.json */,
252EFB2714DA0689004863C8 /* NakedEvents.json */,
25160FD11456F2330060A5C5 /* ArrayOfNestedDictionaries.json */,
25160FD21456F2330060A5C5 /* ArrayOfResults.json */,
@@ -1733,6 +1760,7 @@
25160FE71456F2330060A5C5 /* user.json */,
25160FE81456F2330060A5C5 /* users.json */,
25CAAA9315254E7800CAE5D7 /* ArrayOfHumans.json */,
+ 25119FB5154A34B400C6BC58 /* parents_and_children.json */,
);
path = JSON;
sourceTree = "<group>";
@@ -2220,7 +2248,6 @@
25055B8814EEF32A00B9C4DD /* RKTestFactory.h in Headers */,
25055B8F14EEF40000B9C4DD /* RKMappingTestExpectation.h in Headers */,
25EC1A2C14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.h in Headers */,
- 25EC1A3714F72B0100C3CF3F /* RKInMemoryEntityCache.h in Headers */,
25EC1A3914F72B0900C3CF3F /* RKFetchRequestManagedObjectCache.h in Headers */,
25EC1A3D14F72B2800C3CF3F /* RKInMemoryManagedObjectCache.h in Headers */,
25EC1A4114F72C7200C3CF3F /* RKObjectMappingProvider+CoreData.h in Headers */,
@@ -2234,6 +2261,9 @@
25079C6F151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h in Headers */,
252A202D153471380078F8AD /* NSArray+RKAdditions.h in Headers */,
2572538D155C543000CB05ED /* RKPortCheck.h in Headers */,
+ 259D98541550C69A008C90F5 /* RKEntityByAttributeCache.h in Headers */,
+ 259D985E155218E5008C90F5 /* RKEntityCache.h in Headers */,
+ 25545959155F0527007D7625 /* RKBenchmark.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2342,7 +2372,6 @@
25055B8914EEF32A00B9C4DD /* RKTestFactory.h in Headers */,
25055B9014EEF40000B9C4DD /* RKMappingTestExpectation.h in Headers */,
25EC1A2D14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.h in Headers */,
- 25EC1A3814F72B0200C3CF3F /* RKInMemoryEntityCache.h in Headers */,
25EC1A3A14F72B0A00C3CF3F /* RKFetchRequestManagedObjectCache.h in Headers */,
25EC1A3E14F72B2900C3CF3F /* RKInMemoryManagedObjectCache.h in Headers */,
25EC1A4214F72C7300C3CF3F /* RKObjectMappingProvider+CoreData.h in Headers */,
@@ -2356,6 +2385,9 @@
25079C70151B93DB00266AE7 /* NSEntityDescription+RKAdditions.h in Headers */,
252A20311534714D0078F8AD /* NSArray+RKAdditions.h in Headers */,
2572538E155C543000CB05ED /* RKPortCheck.h in Headers */,
+ 259D98551550C69A008C90F5 /* RKEntityByAttributeCache.h in Headers */,
+ 259D985F155218E5008C90F5 /* RKEntityCache.h in Headers */,
+ 2554595A155F0527007D7625 /* RKBenchmark.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2525,6 +2557,8 @@
25B6E9BE14CF829400B1E881 /* OCMock-Info.plist in Resources */,
252EFB2814DA0689004863C8 /* NakedEvents.json in Resources */,
25CAAA9415254E7800CAE5D7 /* ArrayOfHumans.json in Resources */,
+ 25119FB6154A34B400C6BC58 /* parents_and_children.json in Resources */,
+ 259D983C154F6C90008C90F5 /* benchmark_parents_and_children.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2585,6 +2619,8 @@
25B6E9BF14CF829400B1E881 /* OCMock-Info.plist in Resources */,
252EFB2914DA0689004863C8 /* NakedEvents.json in Resources */,
25CAAA9515254E7800CAE5D7 /* ArrayOfHumans.json in Resources */,
+ 25119FB7154A34B400C6BC58 /* parents_and_children.json in Resources */,
+ 259D983D154F6C90008C90F5 /* benchmark_parents_and_children.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2744,7 +2780,6 @@
25055B8A14EEF32A00B9C4DD /* RKTestFactory.m in Sources */,
25055B9114EEF40000B9C4DD /* RKMappingTestExpectation.m in Sources */,
25EC1A2E14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.m in Sources */,
- 25EC1A3414F72AF000C3CF3F /* RKInMemoryEntityCache.m in Sources */,
25EC1A3B14F72B1300C3CF3F /* RKFetchRequestManagedObjectCache.m in Sources */,
25EC1A3F14F72B3100C3CF3F /* RKInMemoryManagedObjectCache.m in Sources */,
25EC1A4314F72D0D00C3CF3F /* RKObjectMappingProvider+CoreData.m in Sources */,
@@ -2758,6 +2793,9 @@
252A202E153471380078F8AD /* NSArray+RKAdditions.m in Sources */,
25C954A715542A47005C9E08 /* RKTestConstants.m in Sources */,
2572538F155C543000CB05ED /* RKPortCheck.m in Sources */,
+ 259D98561550C69A008C90F5 /* RKEntityByAttributeCache.m in Sources */,
+ 259D9860155218E5008C90F5 /* RKEntityCache.m in Sources */,
+ 2554595B155F0527007D7625 /* RKBenchmark.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2837,13 +2875,14 @@
252EFB0814D98F4D004863C8 /* RKSearchableManagedObjectTest.m in Sources */,
252EFB0A14D98F4D004863C8 /* RKSearchWordObserverTest.m in Sources */,
252EFB0D14D98F76004863C8 /* RKMutableBlockDictionaryTest.m in Sources */,
- 25EC1A4A14F73CA200C3CF3F /* RKInMemoryEntityCacheTest.m in Sources */,
25E36E0215195CED00F9E448 /* RKFetchRequestMappingCacheTest.m in Sources */,
25079C76151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m in Sources */,
25DB7508151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m in Sources */,
252A2034153477870078F8AD /* NSArray+RKAdditionsTest.m in Sources */,
2501405315366000004E0466 /* RKObjectiveCppTest.mm in Sources */,
25A2476E153E667E003240B6 /* RKCacheTest.m in Sources */,
+ 259D985A1550C6BE008C90F5 /* RKEntityByAttributeCacheTest.m in Sources */,
+ 259D986415521B20008C90F5 /* RKEntityCacheTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -2930,7 +2969,6 @@
25055B8B14EEF32A00B9C4DD /* RKTestFactory.m in Sources */,
25055B9214EEF40000B9C4DD /* RKMappingTestExpectation.m in Sources */,
25EC1A2F14F6FDAD00C3CF3F /* RKObjectManager+RKTableController.m in Sources */,
- 25EC1A3614F72AF100C3CF3F /* RKInMemoryEntityCache.m in Sources */,
25EC1A3C14F72B1400C3CF3F /* RKFetchRequestManagedObjectCache.m in Sources */,
25EC1A4014F72B3300C3CF3F /* RKInMemoryManagedObjectCache.m in Sources */,
25EC1A4614F7393E00C3CF3F /* RKObjectMappingProviderContextEntry.m in Sources */,
@@ -2944,6 +2982,9 @@
252A2030153471470078F8AD /* NSArray+RKAdditions.m in Sources */,
25C954A815542A47005C9E08 /* RKTestConstants.m in Sources */,
25725390155C543000CB05ED /* RKPortCheck.m in Sources */,
+ 259D98571550C69A008C90F5 /* RKEntityByAttributeCache.m in Sources */,
+ 259D9861155218E5008C90F5 /* RKEntityCache.m in Sources */,
+ 2554595C155F0527007D7625 /* RKBenchmark.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3023,13 +3064,14 @@
252EFB0914D98F4D004863C8 /* RKSearchableManagedObjectTest.m in Sources */,
252EFB0B14D98F4D004863C8 /* RKSearchWordObserverTest.m in Sources */,
252EFB0E14D98F76004863C8 /* RKMutableBlockDictionaryTest.m in Sources */,
- 25EC1A4B14F73CA200C3CF3F /* RKInMemoryEntityCacheTest.m in Sources */,
25E36E0315195CED00F9E448 /* RKFetchRequestMappingCacheTest.m in Sources */,
25079C77151B952200266AE7 /* NSEntityDescription+RKAdditionsTest.m in Sources */,
25DB7509151BD551009F01AF /* NSManagedObject+ActiveRecordTest.m in Sources */,
252A2035153477870078F8AD /* NSArray+RKAdditionsTest.m in Sources */,
2501405415366000004E0466 /* RKObjectiveCppTest.mm in Sources */,
25A2476F153E667E003240B6 /* RKCacheTest.m in Sources */,
+ 259D985B1550C6BE008C90F5 /* RKEntityByAttributeCacheTest.m in Sources */,
+ 259D986515521B20008C90F5 /* RKEntityCacheTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
2,062 Tests/Fixtures/JSON/benchmark_parents_and_children.json
@@ -0,0 +1,2062 @@
+{
+ "parents": [
+ {
+ "parentID": 1,
+ "name": "Megane Rosenbaum",
+ "children": [
+ {
+ "name": "Miss Jazmin Dooley",
+ "childID": 73
+ },
+ {
+ "name": "Llewellyn Hilll",
+ "childID": 60
+ },
+ {
+ "name": "Adan Vandervort",
+ "childID": 66
+ },
+ {
+ "name": "Joshuah Langworth III",
+ "childID": 81
+ },
+ {
+ "name": "Yazmin Christiansen PhD",
+ "childID": 80
+ },
+ {
+ "name": "Ms. Alan Carter",
+ "childID": 64
+ },
+ {
+ "name": "Mrs. Sally Dickens",
+ "childID": 79
+ },
+ {
+ "name": "Ms. Norval D'Amore",
+ "childID": 72
+ },
+ {
+ "name": "Joan Crooks",
+ "childID": 59
+ },
+ {
+ "name": "Tiara Rolfson",
+ "childID": 84
+ },
+ {
+ "name": "Jacinthe Abernathy",
+ "childID": 77
+ },
+ {
+ "name": "Beaulah Tremblay",
+ "childID": 77
+ },
+ {
+ "name": "Fermin Douglas",
+ "childID": 56
+ },
+ {
+ "name": "Kole Ullrich",
+ "childID": 95
+ },
+ {
+ "name": "Miss Pete Gutkowski",
+ "childID": 65
+ },
+ {
+ "name": "Kamren Kozey",
+ "childID": 52
+ },
+ {
+ "name": "Vladimir Hegmann",
+ "childID": 84
+ },
+ {
+ "name": "Miss Dannie Grant",
+ "childID": 77
+ },
+ {
+ "name": "Miss Jayde Rice",
+ "childID": 59
+ },
+ {
+ "name": "Otilia Hartmann",
+ "childID": 74
+ },
+ {
+ "name": "Donnell Sanford",
+ "childID": 73
+ },
+ {
+ "name": "Bill Hackett",
+ "childID": 94
+ },
+ {
+ "name": "Bulah Sauer IV",
+ "childID": 70
+ },
+ {
+ "name": "Baylee Rippin",
+ "childID": 58
+ }
+ ]
+ },
+ {
+ "parentID": 2,
+ "name": "Brice Maggio",
+ "children": [
+ {
+ "name": "Skye Gerhold",
+ "childID": 53
+ },
+ {
+ "name": "Leonie Price Sr.",
+ "childID": 86