mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-05-11 10:04:16 +08:00
First pass at replacing RKManagedObjectCache with a more flexible block approach that leverages the new resource path pattern matching approach to registering objectMappings.
This commit is contained in:
committed by
Blake Watters
parent
0660f28fc1
commit
1cd22d5b33
258
Code/CoreData/RKInMemoryEntityCache.m
Normal file
258
Code/CoreData/RKInMemoryEntityCache.m
Normal file
@@ -0,0 +1,258 @@
|
||||
//
|
||||
// RKInMemoryEntityCache.m
|
||||
// RestKit
|
||||
//
|
||||
// Created by Jeff Arena on 1/24/12.
|
||||
// Copyright (c) 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
|
||||
|
||||
@implementation RKInMemoryEntityCache
|
||||
|
||||
@synthesize entityCache = _entityCache;
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[_entityCache release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:NSManagedObjectContextObjectsDidChangeNotification
|
||||
object:nil];
|
||||
[_entityCache removeAllObjects];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)cachedObjectsForEntity:(NSEntityDescription *)entity
|
||||
withMapping:(RKManagedObjectMapping *)mapping
|
||||
inContext:(NSManagedObjectContext *)managedObjectContext {
|
||||
NSAssert(entity, @"Cannot retrieve cached objects without an entity");
|
||||
NSAssert(mapping, @"Cannot retrieve cached objects without a mapping");
|
||||
NSAssert(managedObjectContext, @"Cannot retrieve cached objects without a managedObjectContext");
|
||||
|
||||
NSMutableDictionary *cachedObjectsForEntity = [_entityCache objectForKey:entity.name];
|
||||
if (cachedObjectsForEntity == nil) {
|
||||
cachedObjectsForEntity = [self cacheObjectsForEntity:entity
|
||||
withMapping:mapping
|
||||
inContext:managedObjectContext];
|
||||
}
|
||||
return cachedObjectsForEntity;
|
||||
}
|
||||
|
||||
- (NSManagedObject *)cachedObjectForEntity:(NSEntityDescription *)entity
|
||||
withMapping:(RKManagedObjectMapping *)mapping
|
||||
andPrimaryKeyValue:(id)primaryKeyValue
|
||||
inContext:(NSManagedObjectContext *)managedObjectContext {
|
||||
NSAssert(entity, @"Cannot retrieve a cached object without an entity");
|
||||
NSAssert(mapping, @"Cannot retrieve a cached object without a mapping");
|
||||
NSAssert(primaryKeyValue, @"Cannot retrieve a cached object without a primaryKeyValue");
|
||||
NSAssert(managedObjectContext, @"Cannot retrieve a cached object without a managedObjectContext");
|
||||
|
||||
NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
|
||||
withMapping:mapping
|
||||
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 = [primaryKeyValue respondsToSelector:@selector(stringValue)] ? [primaryKeyValue stringValue] : primaryKeyValue;
|
||||
NSManagedObjectID *objectID = [cachedObjectsForEntity objectForKey:lookupValue];
|
||||
NSManagedObject *object = nil;
|
||||
if (objectID) {
|
||||
object = [self objectWithID:objectID inContext:managedObjectContext];
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)cacheObjectsForEntity:(NSEntityDescription *)entity
|
||||
withMapping:(RKManagedObjectMapping *)mapping
|
||||
inContext:(NSManagedObjectContext *)managedObjectContext {
|
||||
NSAssert(entity, @"Cannot cache objects without an entity");
|
||||
NSAssert(mapping, @"Cannot cache objects without a mapping");
|
||||
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 %d %@ objectsIDs to thread local storage", [objectIds count], entity.name);
|
||||
NSMutableDictionary* dictionary = [NSMutableDictionary dictionary];
|
||||
if ([objectIds count] > 0) {
|
||||
BOOL coerceToString = [self shouldCoerceAttributeToString:mapping.primaryKeyAttribute forEntity:entity];
|
||||
for (NSManagedObjectID* theObjectID in objectIds) {
|
||||
NSManagedObject* theObject = [self objectWithID:theObjectID inContext:managedObjectContext];
|
||||
id attributeValue = [theObject valueForKey:mapping.primaryKeyAttribute];
|
||||
// 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];
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
- (void)cacheObject:(NSManagedObject *)managedObject withMapping:(RKManagedObjectMapping *)mapping inContext:(NSManagedObjectContext *)managedObjectContext {
|
||||
NSAssert(managedObject, @"Cannot cache an object without a managedObject");
|
||||
NSAssert(mapping, @"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:mapping.primaryKeyAttribute forEntity:entity];
|
||||
id attributeValue = [managedObject valueForKey:mapping.primaryKeyAttribute];
|
||||
// Coerce to a string if possible
|
||||
attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue;
|
||||
if (attributeValue) {
|
||||
NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
|
||||
withMapping:mapping
|
||||
inContext:managedObjectContext];
|
||||
[cachedObjectsForEntity setObject:objectID forKey:attributeValue];
|
||||
}
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(objectsDidChange:)
|
||||
name:NSManagedObjectContextObjectsDidChangeNotification
|
||||
object:managedObjectContext];
|
||||
}
|
||||
|
||||
- (void)cacheObject:(NSEntityDescription *)entity withMapping:(RKManagedObjectMapping *)mapping andPrimaryKeyValue:(id)primaryKeyValue inContext:(NSManagedObjectContext *)managedObjectContext {
|
||||
NSAssert(entity, @"Cannot cache an object without an entity");
|
||||
NSAssert(mapping, @"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 = [primaryKeyValue respondsToSelector:@selector(stringValue)] ? [primaryKeyValue stringValue] : primaryKeyValue;
|
||||
|
||||
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
|
||||
[fetchRequest setEntity:entity];
|
||||
[fetchRequest setFetchLimit:1];
|
||||
[fetchRequest setPredicate:[NSPredicate predicateWithFormat:@"%@ = %@", mapping.primaryKeyAttribute, 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
|
||||
withMapping:mapping
|
||||
inContext:managedObjectContext];
|
||||
[cachedObjectsForEntity setObject:objectID forKey:lookupValue];
|
||||
}
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(objectsDidChange:)
|
||||
name:NSManagedObjectContextObjectsDidChangeNotification
|
||||
object:managedObjectContext];
|
||||
}
|
||||
|
||||
- (void)expireCacheEntryForObject:(NSManagedObject *)managedObject withMapping:(RKManagedObjectMapping *)mapping inContext:(NSManagedObjectContext *)managedObjectContext {
|
||||
NSAssert(managedObject, @"Cannot expire cache entry for an object without a managedObject");
|
||||
NSAssert(mapping, @"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:mapping.primaryKeyAttribute forEntity:entity];
|
||||
id attributeValue = [managedObject valueForKey:mapping.primaryKeyAttribute];
|
||||
// Coerce to a string if possible
|
||||
attributeValue = coerceToString ? [attributeValue stringValue] : attributeValue;
|
||||
if (attributeValue) {
|
||||
NSMutableDictionary *cachedObjectsForEntity = [self cachedObjectsForEntity:entity
|
||||
withMapping:mapping
|
||||
inContext:managedObjectContext];
|
||||
[cachedObjectsForEntity removeObjectForKey:attributeValue];
|
||||
|
||||
if ([cachedObjectsForEntity count] == 0) {
|
||||
[self expireCacheEntryForEntity:entity];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)expireCacheEntryForEntity:(NSEntityDescription *)entity {
|
||||
NSAssert(entity, @"Cannot expire cache entry for an entity without an entity");
|
||||
RKLogTrace(@"About to expirce 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");
|
||||
return [managedObjectContext objectWithID:objectID];
|
||||
}
|
||||
|
||||
|
||||
#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 expireCacheEntryForEntity:entity];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user