Clean tests, added cached predicate usage for fetch request cache strategy. refs #739

This commit is contained in:
Blake Watters
2012-05-12 19:18:29 -04:00
parent 715ae6cbf7
commit 15f41c6622
14 changed files with 126 additions and 742 deletions

View File

@@ -16,6 +16,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.
*/
@@ -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 File

@@ -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 @@ static char primaryKeyAttributeKey;
// 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 @@ static char primaryKeyAttributeKey;
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 File

@@ -20,7 +20,8 @@
- (id)initWithManagedObjectContext:(NSManagedObjectContext *)context
{
self = [self init];
NSAssert(context, @"Cannot initialize entity cache with a nil context");
self = [super init];
if (self) {
_managedObjectContext = [context retain];
_attributeCaches = [[NSMutableSet alloc] init];
@@ -29,6 +30,11 @@
return self;
}
- (id)init
{
return [self initWithManagedObjectContext:nil];
}
- (void)dealloc
{
[_managedObjectContext release];
@@ -38,6 +44,8 @@
- (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];
@@ -51,12 +59,16 @@
- (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];
@@ -67,6 +79,8 @@
- (NSSet *)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];
@@ -77,6 +91,8 @@
- (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;
@@ -88,6 +104,7 @@
- (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]) {
@@ -105,6 +122,7 @@
- (void)addObject:(NSManagedObject *)object
{
NSAssert(object, @"Cannot add a nil object to the cache");
NSSet *attributeCaches = [self attributeCachesForEntity:object.entity];
for (RKEntityByAttributeCache *cache in attributeCaches) {
[cache addObject:object];
@@ -113,6 +131,7 @@
- (void)removeObject:(NSManagedObject *)object
{
NSAssert(object, @"Cannot remove a nil object from the cache");
NSSet *attributeCaches = [self attributeCachesForEntity:object.entity];
for (RKEntityByAttributeCache *cache in attributeCaches) {
[cache removeObject:object];

View File

@@ -19,18 +19,14 @@
@implementation RKFetchRequestManagedObjectCache
- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
value:(id)primaryKeyValue
inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
{
- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
withPrimaryKeyValue:(id)primaryKeyValue inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext {
NSAssert(entity, @"Cannot find existing managed object without a target class");
NSAssert(primaryKeyAttribute, @"Cannot find existing managed object instance without mapping that defines a primaryKeyAttribute");
NSAssert(primaryKeyValue, @"Cannot find existing managed object by primary key without a value");
NSAssert(managedObjectContext, @"Cannot find existing managed object with a context");
id searchValue = primaryKeyValue;
Class type = [[RKObjectPropertyInspector sharedInspector] typeForProperty:primaryKeyAttribute ofEntity:entity];
Class type = [[RKObjectPropertyInspector sharedInspector] typeForProperty:entity.primaryKeyAttribute ofEntity:entity];
if (type && ([type isSubclassOfClass:[NSString class]] && NO == [primaryKeyValue isKindOfClass:[NSString class]])) {
searchValue = [NSString stringWithFormat:@"%@", primaryKeyValue];
} else if (type && ([type isSubclassOfClass:[NSNumber class]] && NO == [primaryKeyValue isKindOfClass:[NSNumber class]])) {
@@ -39,30 +35,11 @@
}
}
static NSPredicate *predicate = nil;
// if (! predicate) {
predicate = [NSComparisonPredicate
predicateWithLeftExpression:[NSExpression expressionForKeyPath:primaryKeyAttribute]
rightExpression:[NSExpression expressionForConstantValue:searchValue]
modifier:NSDirectPredicateModifier
type:NSEqualToPredicateOperatorType
options:0];
// }
// [NSComparisonPredicate
// predicateWithLeftExpression:
// [NSExpression expressionForEvaluatedObject]
// rightExpression:
// [NSExpression expressionForConstantValue:objectForID]
// modifier:NSDirectPredicateModifier
// type:NSEqualToPredicateOperatorType
// options:0];
// [request setPredicate:predicate];
NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init];
[fetchRequest setEntity:entity];
[fetchRequest setFetchLimit:1];
[fetchRequest setPredicate:predicate];
// [fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", primaryKeyAttribute, searchValue]];
NSPredicate *predicate = [entity predicateForPrimaryKeyAttributeWithValue: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 File

@@ -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 File

@@ -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 File

@@ -47,14 +47,12 @@ static NSString * const RKInMemoryObjectManagedObjectCacheThreadDictionaryKey =
return entityCache;
}
// TODO: This method signature needs to be cleaned up... Remove the primaryKeyAttribute being passed around.
- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
value:(id)primaryKeyValue
withPrimaryKeyValue:(id)primaryKeyValue
inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
{
RKEntityCache *entityCache = [self cacheForEntity:entity inManagedObjectContext:managedObjectContext];
return [entityCache objectForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue];
return [entityCache objectForEntity:entity withAttribute:entity.primaryKeyAttribute value:primaryKeyValue];
}
- (void)didFetchObject:(NSManagedObject *)object
@@ -65,12 +63,16 @@ static NSString * const RKInMemoryObjectManagedObjectCacheThreadDictionaryKey =
- (void)didCreateObject:(NSManagedObject *)object
{
if (! object.entity.primaryKeyAttribute) return;
RKEntityCache *entityCache = [self cacheForEntity:object.entity inManagedObjectContext:object.managedObjectContext];
[entityCache addObject:object];
}
- (void)didDeleteObject:(NSManagedObject *)object
{
if (! object.entity.primaryKeyAttribute) return;
RKEntityCache *entityCache = [self cacheForEntity:object.entity inManagedObjectContext:object.managedObjectContext];
[entityCache removeObject:object];
}

View File

@@ -23,15 +23,13 @@
the primary key attribute and value for the desired object.
@param entity The Core Data entity for the type of object to be retrieved from the cache.
@param primaryKeyAttribute The name of the attribute that acts as the primary key for the entity.
@param primaryKeyValue The value for the primary key attribute of the object to be retrieved from the cache.
@param mmanagedObjectContext The managed object context to be searched for a matching instance.
@return A managed object that is an instance of the given entity with a primary key and value matching
the specified parameters, or nil if no object was found.
*/
- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
value:(id)primaryKeyValue
withPrimaryKeyValue:(id)primaryKeyValue
inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
@optional

View File

@@ -169,7 +169,7 @@
// If we have found the primary key attribute & value, try to find an existing instance to update
if (primaryKeyAttribute && primaryKeyValue && NO == [primaryKeyValue isEqual:[NSNull null]]) {
object = [self.objectStore.cacheStrategy findInstanceOfEntity:entity
withPrimaryKeyAttribute:self.primaryKeyAttribute value:primaryKeyValue inManagedObjectContext:[self.objectStore managedObjectContextForCurrentThread]];
withPrimaryKeyValue:primaryKeyValue inManagedObjectContext:[self.objectStore managedObjectContextForCurrentThread]];
if (object && [self.objectStore.cacheStrategy respondsToSelector:@selector(didFetchObject:)]) {
[self.objectStore.cacheStrategy didFetchObject:object];
@@ -179,7 +179,9 @@
if (object == nil) {
object = [[[NSManagedObject alloc] initWithEntity:entity
insertIntoManagedObjectContext:[_objectStore managedObjectContextForCurrentThread]] autorelease];
[object setValue:primaryKeyValue forKey:primaryKeyAttribute];
if (primaryKeyAttribute && primaryKeyValue && ![primaryKeyValue isEqual:[NSNull null]]) {
[object setValue:primaryKeyValue forKey:primaryKeyAttribute];
}
if ([self.objectStore.cacheStrategy respondsToSelector:@selector(didCreateObject:)]) {
[self.objectStore.cacheStrategy didCreateObject:object];