mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-04-23 20:31:13 +08:00
Refetch the mapping results from the original managed object context before returning to the caller. fixes #1011
Eliminate the thread safe invocation class.
This commit is contained in:
@@ -1,32 +0,0 @@
|
||||
//
|
||||
// RKManagedObjectThreadSafeInvocation.h
|
||||
// RestKit
|
||||
//
|
||||
// Created by Blake Watters on 5/12/11.
|
||||
// Copyright (c) 2009-2012 RestKit. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import <CoreData/CoreData.h>
|
||||
|
||||
@interface RKManagedObjectThreadSafeInvocation : NSInvocation
|
||||
|
||||
@property (nonatomic, strong) NSManagedObjectContext *privateQueueManagedObjectContext;
|
||||
@property (nonatomic, strong) NSManagedObjectContext *mainQueueManagedObjectContext;
|
||||
|
||||
+ (RKManagedObjectThreadSafeInvocation *)invocationWithMethodSignature:(NSMethodSignature *)methodSignature;
|
||||
- (void)setManagedObjectKeyPaths:(NSSet *)keyPaths forArgument:(NSInteger)index;
|
||||
- (void)invokeOnMainThread;
|
||||
|
||||
@end
|
||||
@@ -1,163 +0,0 @@
|
||||
//
|
||||
// RKManagedObjectThreadSafeInvocation.m
|
||||
// RestKit
|
||||
//
|
||||
// Created by Blake Watters on 5/12/11.
|
||||
// Copyright (c) 2009-2012 RestKit. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
#import "RKManagedObjectThreadSafeInvocation.h"
|
||||
#import "RKLog.h"
|
||||
|
||||
// Set Logging Component
|
||||
#undef RKLogComponent
|
||||
#define RKLogComponent RKlcl_cRestKitCoreData
|
||||
|
||||
@interface RKManagedObjectThreadSafeInvocation ()
|
||||
@property (nonatomic, strong) NSMutableDictionary *argumentKeyPaths;
|
||||
@end
|
||||
|
||||
@implementation RKManagedObjectThreadSafeInvocation
|
||||
|
||||
|
||||
+ (RKManagedObjectThreadSafeInvocation *)invocationWithMethodSignature:(NSMethodSignature *)methodSignature
|
||||
{
|
||||
return (RKManagedObjectThreadSafeInvocation *)[super invocationWithMethodSignature:methodSignature];
|
||||
}
|
||||
|
||||
- (void)setManagedObjectKeyPaths:(NSSet *)keyPaths forArgument:(NSInteger)index
|
||||
{
|
||||
if (nil == _argumentKeyPaths) {
|
||||
self.argumentKeyPaths = [NSMutableDictionary dictionary];
|
||||
}
|
||||
|
||||
NSNumber *argumentIndex = [NSNumber numberWithInteger:index];
|
||||
[self.argumentKeyPaths setObject:keyPaths forKey:argumentIndex];
|
||||
}
|
||||
|
||||
- (void)setValue:(id)value forKeyPathOrKey:(NSString *)keyPath object:(id)object
|
||||
{
|
||||
[object setValue:value forKeyPath:keyPath];
|
||||
|
||||
id testValue = [object valueForKeyPath:keyPath];
|
||||
if (![value isEqual:testValue]) {
|
||||
[object setValue:value forKey:keyPath];
|
||||
testValue = [object valueForKeyPath:keyPath];
|
||||
|
||||
NSAssert([value isEqual:testValue], @"Could not set value");
|
||||
}
|
||||
}
|
||||
|
||||
- (void)serializeManagedObjectsForArgument:(id)argument withKeyPaths:(NSSet *)keyPaths
|
||||
{
|
||||
for (NSString *keyPath in keyPaths) {
|
||||
id value = [argument valueForKeyPath:keyPath];
|
||||
if ([value isKindOfClass:[NSManagedObject class]]) {
|
||||
NSManagedObjectID *objectID = [(NSManagedObject *)value objectID];
|
||||
[self setValue:objectID forKeyPathOrKey:keyPath object:argument];
|
||||
} else if ([value respondsToSelector:@selector(allObjects)]) {
|
||||
id collection = [[[[value class] alloc] init] mutableCopy];
|
||||
for (id subObject in value) {
|
||||
if ([subObject isKindOfClass:[NSManagedObject class]]) {
|
||||
[collection addObject:[(NSManagedObject *)subObject objectID]];
|
||||
} else {
|
||||
[collection addObject:subObject];
|
||||
}
|
||||
}
|
||||
|
||||
[self setValue:collection forKeyPathOrKey:keyPath object:argument];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)deserializeManagedObjectIDsForArgument:(id)argument withKeyPaths:(NSSet *)keyPaths
|
||||
{
|
||||
NSAssert(self.mainQueueManagedObjectContext, @"Managed object context cannot be nil");
|
||||
for (NSString *keyPath in keyPaths) {
|
||||
id value = [argument valueForKeyPath:keyPath];
|
||||
if ([value isKindOfClass:[NSManagedObjectID class]]) {
|
||||
__block NSManagedObject *managedObject = nil;
|
||||
__block NSError *error;
|
||||
[self.mainQueueManagedObjectContext performBlockAndWait:^{
|
||||
managedObject = [self.mainQueueManagedObjectContext existingObjectWithID:(NSManagedObjectID *)value error:&error];
|
||||
}];
|
||||
NSAssert(managedObject, @"Expected managed object for ID %@, got nil", value);
|
||||
[self setValue:managedObject forKeyPathOrKey:keyPath object:argument];
|
||||
} else if ([value respondsToSelector:@selector(allObjects)]) {
|
||||
id collection = [[[[value class] alloc] init] mutableCopy];
|
||||
for (id subObject in value) {
|
||||
if ([subObject isKindOfClass:[NSManagedObjectID class]]) {
|
||||
__block NSManagedObject *managedObject = nil;
|
||||
__block NSError *error;
|
||||
[self.mainQueueManagedObjectContext performBlockAndWait:^{
|
||||
managedObject = [self.mainQueueManagedObjectContext existingObjectWithID:(NSManagedObjectID *)subObject error:&error];
|
||||
}];
|
||||
NSAssert(managedObject, @"Expected managed object for ID %@, got nil: %@", subObject, error);
|
||||
[collection addObject:managedObject];
|
||||
} else {
|
||||
[collection addObject:subObject];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[self setValue:collection forKeyPathOrKey:keyPath object:argument];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)serializeManagedObjects
|
||||
{
|
||||
for (NSNumber *argumentIndex in _argumentKeyPaths) {
|
||||
NSSet *managedKeyPaths = [_argumentKeyPaths objectForKey:argumentIndex];
|
||||
id argument = nil;
|
||||
[self getArgument:&argument atIndex:[argumentIndex intValue]];
|
||||
if (argument) {
|
||||
[self serializeManagedObjectsForArgument:argument withKeyPaths:managedKeyPaths];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)deserializeManagedObjects
|
||||
{
|
||||
for (NSNumber *argumentIndex in _argumentKeyPaths) {
|
||||
NSSet *managedKeyPaths = [_argumentKeyPaths objectForKey:argumentIndex];
|
||||
id argument = nil;
|
||||
[self getArgument:&argument atIndex:[argumentIndex intValue]];
|
||||
if (argument) {
|
||||
[self deserializeManagedObjectIDsForArgument:argument withKeyPaths:managedKeyPaths];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)performInvocationOnMainThread
|
||||
{
|
||||
[self deserializeManagedObjects];
|
||||
[self invoke];
|
||||
}
|
||||
|
||||
- (void)invokeOnMainThread
|
||||
{
|
||||
[self serializeManagedObjects];
|
||||
if ([NSThread isMainThread]) {
|
||||
[self performInvocationOnMainThread];
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
[self performInvocationOnMainThread];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -46,12 +46,6 @@
|
||||
|
||||
Primary key attributes are used in conjunction with the `RKManagedObjectCaching` protocol. Each managed object request operation is associated with an object conforming to the `RKManagedObjectCaching` protocol via the `managedObjectCache` proeprty. This cache is consulted during mapping to find existing objects and when establishing relationships between entities by primary key. Please see the documentation accompanying `RKManagedObjectCaching` and `RKConnectionMapping` for more information.
|
||||
|
||||
## Fetching Result Objects
|
||||
|
||||
When a `completionBlock` is configured for an instance of `RKManagedObjectRequestOperation` additional work is performed before the mapping result is returned to the caller. Because mapping internally occurs on a private managed object context with the `NSPrivateQueueConcurrencyType` concurrency type, attempts to directly access the `NSManagedObject` instances contained within the `RKMappingResult` would violate the threading constraints imposed by Core Data. As such, before the mapping result is returned to the caller in a completion block the objects are re-fetched from the `managedObjectContext`. Please see `RKManagedObjectThreadSafeInvocation` for details about the implementation.
|
||||
|
||||
TODO: Is this necessary?
|
||||
|
||||
## Deleting Managed Objects for `DELETE` requests
|
||||
|
||||
`RKManagedObjectRequestOperation` adds special behavior to `DELETE` requests. Upon retrieving a successful (2xx status code) response for a `DELETE`, the operation will invoke `deleteObject:` with the operations `targetObject` on the managed object context. This will delete the target object from the local store in conjunction the successfully deleted remote representation.
|
||||
|
||||
@@ -39,13 +39,44 @@ NSFetchRequest *RKFetchRequestFromBlocksWithURL(NSArray *fetchRequestBlocks, NSU
|
||||
return fetchRequest;
|
||||
}
|
||||
|
||||
static NSDictionary *RKDictionaryOfManagedObjectsInContextFromDictionaryOfManagedObjects(NSDictionary *dictionaryOfManagedObjects, NSManagedObjectContext *managedObjectContext)
|
||||
{
|
||||
NSMutableDictionary *newDictionary = [[NSMutableDictionary alloc] initWithCapacity:[dictionaryOfManagedObjects count]];
|
||||
[managedObjectContext performBlockAndWait:^{
|
||||
NSError *error = nil;
|
||||
for (NSString *key in dictionaryOfManagedObjects) {
|
||||
id value = dictionaryOfManagedObjects[key];
|
||||
if ([value isKindOfClass:[NSArray class]]) {
|
||||
NSMutableArray *newValue = [[NSMutableArray alloc] initWithCapacity:[value count]];
|
||||
for (__strong id object in value) {
|
||||
if ([object isKindOfClass:[NSManagedObject class]]) {
|
||||
object = [managedObjectContext existingObjectWithID:[object objectID] error:&error];
|
||||
NSCAssert(object, @"Failed to find existing object with ID %@ in context %@: %@", [object objectID], managedObjectContext, error);
|
||||
}
|
||||
|
||||
[newValue addObject:object];
|
||||
}
|
||||
value = [newValue copy];
|
||||
} else if ([value isKindOfClass:[NSManagedObject class]]) {
|
||||
value = [managedObjectContext existingObjectWithID:[value objectID] error:&error];
|
||||
NSCAssert(value, @"Failed to find existing object with ID %@ in context %@: %@", [value objectID], managedObjectContext, error);
|
||||
}
|
||||
|
||||
[newDictionary setValue:value forKey:key];
|
||||
}
|
||||
}];
|
||||
|
||||
return newDictionary;
|
||||
}
|
||||
|
||||
@interface RKManagedObjectRequestOperation () <RKMapperOperationDelegate>
|
||||
// Core Data specific
|
||||
@property (readwrite, nonatomic, strong) NSManagedObjectContext *privateContext;
|
||||
@property (readwrite, nonatomic, copy) NSManagedObjectID *targetObjectID;
|
||||
@property (readwrite, nonatomic, strong) NSMutableDictionary *managedObjectsByKeyPath;
|
||||
@property (readwrite, nonatomic, strong) NSError *error;
|
||||
@property (nonatomic, strong) NSManagedObjectContext *privateContext;
|
||||
@property (nonatomic, copy) NSManagedObjectID *targetObjectID;
|
||||
@property (nonatomic, strong) NSMutableDictionary *managedObjectsByKeyPath;
|
||||
@property (nonatomic, strong) RKManagedObjectResponseMapperOperation *responseMapperOperation;
|
||||
@property (nonatomic, strong, readwrite) NSError *error;
|
||||
@property (nonatomic, strong, readwrite) RKMappingResult *mappingResult;
|
||||
@end
|
||||
|
||||
@implementation RKManagedObjectRequestOperation
|
||||
@@ -288,6 +319,10 @@ NSFetchRequest *RKFetchRequestFromBlocksWithURL(NSArray *fetchRequestBlocks, NSU
|
||||
}
|
||||
success = [self saveContext:&error];
|
||||
if (! success) self.error = error;
|
||||
|
||||
// Refetch the mapping results from the externally configured context
|
||||
NSDictionary *resultsDictionaryFromOriginalContext = RKDictionaryOfManagedObjectsInContextFromDictionaryOfManagedObjects([self.mappingResult dictionary], self.managedObjectContext);
|
||||
self.mappingResult = [[RKMappingResult alloc] initWithDictionary:resultsDictionaryFromOriginalContext];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user