mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-04-02 09:31:32 +08:00
Since OM 2.0 connection of relationships happened during the object mapping operation instead of aggregately at the end of the process. In this commit, we have introduced a lightweight queue for deferring portions of the mapping operation until a larger aggregate mapping has completed. The changes are as follows: * Introduced RKMappingOperationQueue for queueing portions of mapping. This is a synchronous queue modeled off of NSOperationQueue that does NOT use threading (for Core Data friendliness). * RKObjectMappingOperation now has a RKMappingOperationQueue queue property that defaults to nil * RKObjectMappingOperation instances built via RKObjectMapper will has a mapping operation queue assigned to the property. * If a queue is present, RKManagedObjectMappingOperation will use it to defer the connection of relationships. * At the end of an RKObjectMapper process, the mapping operation queue used by all mapping operations created during the process will be executed. This allows all relationships to be connected after all object creation has completed. The queue is general purpose, though currently only used for the connection of relationships.
117 lines
5.8 KiB
Objective-C
117 lines
5.8 KiB
Objective-C
//
|
|
// RKManagedObjectMappingOperation.m
|
|
// RestKit
|
|
//
|
|
// Created by Blake Watters on 5/31/11.
|
|
// Copyright 2011 Two Toasters
|
|
//
|
|
// 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 "RKManagedObjectMappingOperation.h"
|
|
#import "RKManagedObjectMapping.h"
|
|
#import "NSManagedObject+ActiveRecord.h"
|
|
#import "../Support/RKLog.h"
|
|
|
|
// Set Logging Component
|
|
#undef RKLogComponent
|
|
#define RKLogComponent lcl_cRestKitCoreData
|
|
|
|
/**
|
|
Progressively enhance the RKObjectMappingOperation base class to inject Core Data
|
|
specifics without leaking into the object mapper abstractions
|
|
*/
|
|
@implementation RKObjectMappingOperation (CoreData)
|
|
|
|
/*
|
|
Trampoline the initialization through RKManagedObjectMapping so the mapper uses RKManagedObjectMappingOperation
|
|
at the right moments
|
|
*/
|
|
+ (RKObjectMappingOperation*)mappingOperationFromObject:(id)sourceObject toObject:(id)destinationObject withMapping:(RKObjectMapping*)objectMapping {
|
|
if ([objectMapping isKindOfClass:[RKManagedObjectMapping class]]) {
|
|
return [[[RKManagedObjectMappingOperation alloc] initWithSourceObject:sourceObject destinationObject:destinationObject mapping:objectMapping] autorelease];
|
|
}
|
|
|
|
return [[[RKObjectMappingOperation alloc] initWithSourceObject:sourceObject destinationObject:destinationObject mapping:objectMapping] autorelease];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RKManagedObjectMappingOperation
|
|
|
|
// TODO: Move this to a better home to take exposure out of the mapper
|
|
- (Class)operationClassForMapping:(RKObjectMapping*)mapping {
|
|
Class managedMappingClass = NSClassFromString(@"RKManagedObjectMapping");
|
|
Class managedMappingOperationClass = NSClassFromString(@"RKManagedObjectMappingOperation");
|
|
if (managedMappingClass != nil && [mapping isMemberOfClass:managedMappingClass]) {
|
|
return managedMappingOperationClass;
|
|
}
|
|
|
|
return [RKObjectMappingOperation class];
|
|
}
|
|
|
|
- (void)connectRelationship:(NSString *)relationshipName {
|
|
NSDictionary* relationshipsAndPrimaryKeyAttributes = [(RKManagedObjectMapping*)self.objectMapping relationshipsAndPrimaryKeyAttributes];
|
|
NSString* primaryKeyAttribute = [relationshipsAndPrimaryKeyAttributes objectForKey:relationshipName];
|
|
RKObjectRelationshipMapping* relationshipMapping = [self.objectMapping mappingForKeyPath:relationshipName];
|
|
id<RKObjectMappingDefinition> mapping = relationshipMapping.mapping;
|
|
NSAssert(mapping, @"Attempted to connect relationship for keyPath '%@' without a relationship mapping defined.");
|
|
if (! [mapping isKindOfClass:[RKObjectMapping class]]) {
|
|
RKLogWarning(@"Can only connect relationships for RKObjectMapping relationships. Found %@: Skipping...", NSStringFromClass([mapping class]));
|
|
return;
|
|
}
|
|
RKObjectMapping* objectMapping = (RKObjectMapping*)mapping;
|
|
NSAssert(relationshipMapping, @"Unable to find relationship mapping '%@' to connect by primaryKey", relationshipName);
|
|
NSAssert([relationshipMapping isKindOfClass:[RKObjectRelationshipMapping class]], @"Expected mapping for %@ to be a relationship mapping", relationshipName);
|
|
NSAssert([relationshipMapping.mapping isKindOfClass:[RKManagedObjectMapping class]], @"Can only connect RKManagedObjectMapping relationships");
|
|
NSString* primaryKeyAttributeOfRelatedObject = [(RKManagedObjectMapping*)objectMapping primaryKeyAttribute];
|
|
NSAssert(primaryKeyAttributeOfRelatedObject, @"Cannot connect relationship: mapping for %@ has no primary key attribute specified", NSStringFromClass(objectMapping.objectClass));
|
|
id valueOfLocalPrimaryKeyAttribute = [self.destinationObject valueForKey:primaryKeyAttribute];
|
|
RKLogDebug(@"Connecting relationship at keyPath '%@' to object with primaryKey attribute '%@'", relationshipName, primaryKeyAttributeOfRelatedObject);
|
|
if (valueOfLocalPrimaryKeyAttribute) {
|
|
id relatedObject = [objectMapping.objectClass findFirstByAttribute:primaryKeyAttributeOfRelatedObject withValue:valueOfLocalPrimaryKeyAttribute];
|
|
if (relatedObject) {
|
|
RKLogTrace(@"Connected relationship '%@' to object with primary key value '%@': %@", relationshipName, valueOfLocalPrimaryKeyAttribute, relatedObject);
|
|
} else {
|
|
RKLogTrace(@"Failed to find object to connect relationship '%@' with primary key value '%@'", relationshipName, valueOfLocalPrimaryKeyAttribute);
|
|
}
|
|
[self.destinationObject setValue:relatedObject forKey:relationshipName];
|
|
} else {
|
|
RKLogTrace(@"Failed to find primary key value for attribute '%@'", primaryKeyAttribute);
|
|
}
|
|
}
|
|
|
|
- (void)connectRelationships {
|
|
if ([self.objectMapping isKindOfClass:[RKManagedObjectMapping class]]) {
|
|
NSDictionary* relationshipsAndPrimaryKeyAttributes = [(RKManagedObjectMapping*)self.objectMapping relationshipsAndPrimaryKeyAttributes];
|
|
for (NSString* relationshipName in relationshipsAndPrimaryKeyAttributes) {
|
|
if (self.queue) {
|
|
RKLogTrace(@"Enqueueing relationship connection using operation queue");
|
|
[self.queue addOperationWithBlock:^{
|
|
[self connectRelationship:relationshipName];
|
|
}];
|
|
} else {
|
|
[self connectRelationship:relationshipName];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL)performMapping:(NSError**)error {
|
|
BOOL success = [super performMapping:error];
|
|
[self connectRelationships];
|
|
return success;
|
|
}
|
|
|
|
@end
|