mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-04-28 20:55:32 +08:00
Migrated to a single entity cache for the primary MOC instead of one per thread
This commit is contained in:
@@ -139,21 +139,23 @@
|
||||
- (BOOL)containsObjectWithAttributeValue:(id)attributeValue;
|
||||
|
||||
/**
|
||||
Returns the first object with a matching value for the cache key attribute.
|
||||
Returns the first object with a matching value for the cache key attribute
|
||||
in a given managed object context.
|
||||
|
||||
@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;
|
||||
- (NSManagedObject *)objectWithAttributeValue:(id)attributeValue inContext:(NSManagedObjectContext *)context;
|
||||
|
||||
/**
|
||||
Returns the collection of objects with a matching value for the cache key attribute.
|
||||
|
||||
Returns the collection of objects with a matching value for the cache key attribute
|
||||
in a given managed object context.
|
||||
|
||||
@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;
|
||||
- (NSArray *)objectsWithAttributeValue:(id)attributeValue inContext:(NSManagedObjectContext *)context;
|
||||
|
||||
///-----------------------------------------------------------------------------
|
||||
/// @name Managing Cached Objects
|
||||
|
||||
@@ -45,11 +45,7 @@
|
||||
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:)
|
||||
@@ -85,7 +81,7 @@
|
||||
|
||||
- (NSUInteger)countWithAttributeValue:(id)attributeValue
|
||||
{
|
||||
return [[self objectsWithAttributeValue:attributeValue] count];
|
||||
return [[self objectsWithAttributeValue:attributeValue inContext:self.managedObjectContext] count];
|
||||
}
|
||||
|
||||
- (BOOL)shouldCoerceAttributeToString:(NSString *)attributeValue
|
||||
@@ -150,13 +146,7 @@
|
||||
return (self.attributeValuesToObjectIDs != nil);
|
||||
}
|
||||
|
||||
- (NSManagedObject *)objectWithAttributeValue:(id)attributeValue
|
||||
{
|
||||
NSArray *objects = [self objectsWithAttributeValue:attributeValue];
|
||||
return ([objects count] > 0) ? [objects objectAtIndex:0] : nil;
|
||||
}
|
||||
|
||||
- (NSManagedObject *)objectForObjectID:(NSManagedObjectID *)objectID
|
||||
- (NSManagedObject *)objectForObjectID:(NSManagedObjectID *)objectID inContext:(NSManagedObjectContext *)context
|
||||
{
|
||||
/*
|
||||
NOTE:
|
||||
@@ -164,34 +154,58 @@
|
||||
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];
|
||||
__block NSError *error = nil;
|
||||
__block NSManagedObject *object;
|
||||
[context performBlockAndWait:^{
|
||||
object = [context existingObjectWithID:objectID error:&error];
|
||||
}];
|
||||
if (! object) {
|
||||
if (error) {
|
||||
RKLogError(@"Failed to retrieve managed object with ID %@. Error %@\n%@", objectID, [error localizedDescription], [error userInfo]);
|
||||
}
|
||||
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
- (NSArray *)objectsWithAttributeValue:(id)attributeValue
|
||||
- (NSManagedObject *)objectWithAttributeValue:(id)attributeValue inContext:(NSManagedObjectContext *)context
|
||||
{
|
||||
NSArray *objects = [self objectsWithAttributeValue:attributeValue inContext:context];
|
||||
return ([objects count] > 0) ? [objects objectAtIndex:0] : nil;
|
||||
}
|
||||
|
||||
- (NSArray *)objectsWithAttributeValue:(id)attributeValue inContext:(NSManagedObjectContext *)context
|
||||
{
|
||||
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 objectForObjectID:objectID];
|
||||
if (object) {
|
||||
[objects addObject:object];
|
||||
} else {
|
||||
RKLogDebug(@"Evicting objectID association for attribute '%@'=>'%@' of Entity '%@': %@", self.attribute, attributeValue, self.entity.name, objectID);
|
||||
[self removeObjectID:objectID forAttributeValue:attributeValue];
|
||||
}
|
||||
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
|
||||
fetchRequest.entity = self.entity;
|
||||
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"self in %@", objectIDs];
|
||||
|
||||
__block NSArray *objects;
|
||||
__block NSError *error;
|
||||
[context performBlockAndWait:^{
|
||||
objects = [context executeFetchRequest:fetchRequest error:&error];
|
||||
}];
|
||||
if (! objects) {
|
||||
RKLogWarning(@"Failed to retrieve cached objects. Execution failed for fetch request: %@", fetchRequest);
|
||||
RKLogCoreDataError(error);
|
||||
}
|
||||
|
||||
|
||||
// NSMutableArray *objects = [NSMutableArray arrayWithCapacity:[objectIDs count]];
|
||||
// for (NSManagedObjectID *objectID in objectIDs) {
|
||||
// NSManagedObject *object = [self objectForObjectID:objectID inContext:context];
|
||||
// if (object) {
|
||||
// [objects addObject:object];
|
||||
// } else {
|
||||
// RKLogDebug(@"Evicting objectID association for attribute '%@'=>'%@' of Entity '%@': %@", self.attribute, attributeValue, self.entity.name, objectID);
|
||||
// [self removeObjectID:objectID forAttributeValue:attributeValue];
|
||||
// }
|
||||
// }
|
||||
|
||||
return objects;
|
||||
}
|
||||
@@ -271,7 +285,7 @@
|
||||
{
|
||||
// Coerce to a string if possible
|
||||
attributeValue = [self shouldCoerceAttributeToString:attributeValue] ? [attributeValue stringValue] : attributeValue;
|
||||
return [[self objectsWithAttributeValue:attributeValue] count] > 0;
|
||||
return [[self objectsWithAttributeValue:attributeValue inContext:self.managedObjectContext] count] > 0;
|
||||
}
|
||||
|
||||
- (BOOL)containsObject:(NSManagedObject *)object
|
||||
@@ -306,15 +320,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (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];
|
||||
|
||||
// TODO: We should not do this... better strategy?
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning:(NSNotification *)notification
|
||||
{
|
||||
[self flush];
|
||||
|
||||
@@ -65,10 +65,11 @@
|
||||
@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.
|
||||
@param context The managed object from which to retrieve the cached results.
|
||||
@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;
|
||||
- (NSManagedObject *)objectForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue inContext:(NSManagedObjectContext *)context;
|
||||
|
||||
/**
|
||||
Retrieves all cached instances of a given entity where the specified attribute matches the given value.
|
||||
@@ -76,10 +77,11 @@
|
||||
@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.
|
||||
@param context The managed object from which to retrieve the cached results.
|
||||
@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;
|
||||
- (NSArray *)objectsForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue inContext:(NSManagedObjectContext *)context;
|
||||
|
||||
///-----------------------------------------------------------------------------
|
||||
// @name Accessing Underlying Caches
|
||||
|
||||
@@ -65,25 +65,25 @@
|
||||
return (attributeCache && attributeCache.isLoaded);
|
||||
}
|
||||
|
||||
- (NSManagedObject *)objectForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue
|
||||
- (NSManagedObject *)objectForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue inContext:(NSManagedObjectContext *)context
|
||||
{
|
||||
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 [attributeCache objectWithAttributeValue:attributeValue inContext:context];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSArray *)objectsForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue
|
||||
- (NSArray *)objectsForEntity:(NSEntityDescription *)entity withAttribute:(NSString *)attributeName value:(id)attributeValue inContext:(NSManagedObjectContext *)context
|
||||
{
|
||||
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 [attributeCache objectsWithAttributeValue:attributeValue inContext:context];
|
||||
}
|
||||
|
||||
return [NSSet set];
|
||||
|
||||
@@ -15,4 +15,14 @@
|
||||
*/
|
||||
@interface RKInMemoryManagedObjectCache : NSObject <RKManagedObjectCaching>
|
||||
|
||||
/**
|
||||
Initializes the receiver with a managed object context that is to be observed
|
||||
and used to populate the in memory cache. The receiver may then be used to fulfill
|
||||
cache requests for child contexts of the given managed object context.
|
||||
|
||||
@param managedObjectContext The managed object context with which to initialize the receiver.
|
||||
@return The receiver, initialized with the given managed object context.
|
||||
*/
|
||||
- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext;
|
||||
|
||||
@end
|
||||
|
||||
@@ -15,29 +15,36 @@
|
||||
#undef RKLogComponent
|
||||
#define RKLogComponent lcl_cRestKitCoreData
|
||||
|
||||
static NSString * const RKInMemoryObjectManagedObjectCacheThreadDictionaryKey = @"RKInMemoryObjectManagedObjectCacheThreadDictionaryKey";
|
||||
//static NSString * const RKInMemoryObjectManagedObjectCacheThreadDictionaryKey = @"RKInMemoryObjectManagedObjectCacheThreadDictionaryKey";
|
||||
|
||||
@interface RKInMemoryManagedObjectCache ()
|
||||
@property (nonatomic, retain) RKEntityCache *entityCache;
|
||||
@end
|
||||
|
||||
@implementation RKInMemoryManagedObjectCache
|
||||
|
||||
- (RKEntityCache *)cacheForEntity:(NSEntityDescription *)entity inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
|
||||
- (id)initWithManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
|
||||
{
|
||||
NSAssert(entity, @"Cannot find existing managed object without a target class");
|
||||
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]];
|
||||
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];
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.entityCache = [[[RKEntityCache alloc] initWithManagedObjectContext:managedObjectContext] autorelease];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
return entityCache;
|
||||
- (id)init
|
||||
{
|
||||
@throw [NSException exceptionWithName:NSInternalInconsistencyException
|
||||
reason:[NSString stringWithFormat:@"%@ Failed to call designated initializer. Invoke initWithManagedObjectContext: instead.",
|
||||
NSStringFromClass([self class])]
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_entityCache release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
|
||||
@@ -45,15 +52,15 @@ static NSString * const RKInMemoryObjectManagedObjectCacheThreadDictionaryKey =
|
||||
value:(id)primaryKeyValue
|
||||
inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
|
||||
{
|
||||
RKEntityCache *entityCache = [self cacheForEntity:entity inManagedObjectContext:managedObjectContext];
|
||||
if (! [entityCache isEntity:entity cachedByAttribute:primaryKeyAttribute]) {
|
||||
NSAssert(self.entityCache, @"Entity cache cannot be nil.");
|
||||
if (! [self.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];
|
||||
[self.entityCache cacheObjectsForEntity:entity byAttribute:primaryKeyAttribute];
|
||||
RKEntityByAttributeCache *attributeCache = [self.entityCache attributeCacheForEntity:entity attribute:primaryKeyAttribute];
|
||||
RKLogTrace(@"Cached %ld objects", (long)[attributeCache count]);
|
||||
}
|
||||
|
||||
return [entityCache objectForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue];
|
||||
return [self.entityCache objectForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue inContext:managedObjectContext];
|
||||
}
|
||||
|
||||
- (NSArray *)findInstancesOfEntity:(NSEntityDescription *)entity
|
||||
@@ -61,33 +68,31 @@ static NSString * const RKInMemoryObjectManagedObjectCacheThreadDictionaryKey =
|
||||
value:(id)primaryKeyValue
|
||||
inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
|
||||
{
|
||||
RKEntityCache *entityCache = [self cacheForEntity:entity inManagedObjectContext:managedObjectContext];
|
||||
if (! [entityCache isEntity:entity cachedByAttribute:primaryKeyAttribute]) {
|
||||
NSAssert(self.entityCache, @"Entity cache cannot be nil.");
|
||||
|
||||
if (! [self.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];
|
||||
[self.entityCache cacheObjectsForEntity:entity byAttribute:primaryKeyAttribute];
|
||||
RKEntityByAttributeCache *attributeCache = [self.entityCache attributeCacheForEntity:entity attribute:primaryKeyAttribute];
|
||||
RKLogTrace(@"Cached %ld objects", (long) [attributeCache count]);
|
||||
}
|
||||
|
||||
return [entityCache objectsForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue];
|
||||
return [self.entityCache objectsForEntity:entity withAttribute:primaryKeyAttribute value:primaryKeyValue inContext:managedObjectContext];
|
||||
}
|
||||
|
||||
- (void)didFetchObject:(NSManagedObject *)object
|
||||
{
|
||||
RKEntityCache *entityCache = [self cacheForEntity:object.entity inManagedObjectContext:object.managedObjectContext];
|
||||
[entityCache addObject:object];
|
||||
[self.entityCache addObject:object];
|
||||
}
|
||||
|
||||
- (void)didCreateObject:(NSManagedObject *)object
|
||||
{
|
||||
RKEntityCache *entityCache = [self cacheForEntity:object.entity inManagedObjectContext:object.managedObjectContext];
|
||||
[entityCache addObject:object];
|
||||
[self.entityCache addObject:object];
|
||||
}
|
||||
|
||||
- (void)didDeleteObject:(NSManagedObject *)object
|
||||
{
|
||||
RKEntityCache *entityCache = [self cacheForEntity:object.entity inManagedObjectContext:object.managedObjectContext];
|
||||
[entityCache removeObject:object];
|
||||
[self.entityCache removeObject:object];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -141,7 +141,7 @@ static RKManagedObjectStore *defaultStore = nil;
|
||||
[self createPersistentStoreCoordinator];
|
||||
[self createManagedObjectContexts];
|
||||
|
||||
_cacheStrategy = [RKInMemoryManagedObjectCache new];
|
||||
_cacheStrategy = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:self.primaryManagedObjectContext];
|
||||
|
||||
// Ensure there is a search word observer
|
||||
[RKSearchWordObserver sharedObserver];
|
||||
|
||||
Reference in New Issue
Block a user