Merge branch 'development' of github.com:CFKevinRef/RestKit into CFKevinRef-development

This commit is contained in:
Blake Watters
2012-11-07 21:59:38 -05:00
13 changed files with 175 additions and 72 deletions

View File

@@ -94,7 +94,7 @@
return NO;
}
Class attributeType = [[RKPropertyInspector sharedInspector] typeForProperty:self.attribute ofEntity:self.entity];
Class attributeType = [[RKPropertyInspector sharedInspector] classForPropertyNamed:self.attribute ofEntity:self.entity];
return [attributeType instancesRespondToSelector:@selector(stringValue)];
}

View File

@@ -137,7 +137,7 @@
{
Class propertyClass = [super classForProperty:propertyName];
if (! propertyClass) {
propertyClass = [[RKPropertyInspector sharedInspector] typeForProperty:propertyName ofEntity:self.entity];
propertyClass = [[RKPropertyInspector sharedInspector] classForPropertyNamed:propertyName ofEntity:self.entity];
}
return propertyClass;

View File

@@ -28,7 +28,7 @@
NSAssert(managedObjectContext, @"Cannot find existing managed object with a nil context");
id searchValue = primaryKeyValue;
Class type = [[RKPropertyInspector sharedInspector] typeForProperty:primaryKeyAttribute ofEntity:entity];
Class type = [[RKPropertyInspector sharedInspector] classForPropertyNamed: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]])) {

View File

@@ -20,13 +20,26 @@
#import "RKPropertyInspector.h"
/**
The `CoreData` category augments the `RKPropertyInspector` class with support for introspecting the property types for `NSManagedObject` and `NSEntityDescription` objects.
*/
@interface RKPropertyInspector (CoreData)
- (NSDictionary *)propertyNamesAndTypesForEntity:(NSEntityDescription *)entity;
/**
Returns a dictionary wherein the keys are the names of attribute and relationship properties and the values are the class used to represent the corresponding property for a given entity.
@param entity The entity to retrieve the properties names and classes of.
@return A dictionary containing the names and classes of the given entity.
*/
- (NSDictionary *)propertyNamesAndClassesForEntity:(NSEntityDescription *)entity;
/**
Returns the Class type of the specified property on the object class
Returns the class used to represent the property with the given name on the given entity.
@param propertyName The name of the property to retrieve the class for.
@param entity The entity containing the property to retrieve the class for.
@return The class used to represent the property.
*/
- (Class)typeForProperty:(NSString *)propertyName ofEntity:(NSEntityDescription *)entity;
- (Class)classForPropertyNamed:(NSString *)propertyName ofEntity:(NSEntityDescription *)entity;
@end

View File

@@ -19,11 +19,11 @@
//
#import <CoreData/CoreData.h>
#import <objc/message.h>
#import "RKPropertyInspector+CoreData.h"
#import "RKLog.h"
#import "RKObjectUtilities.h"
#import "RKMacros.h"
#import <objc/message.h>
RK_FIX_CATEGORY_BUG(RKPropertyInspector_CoreData)
@@ -33,7 +33,7 @@ RK_FIX_CATEGORY_BUG(RKPropertyInspector_CoreData)
@implementation RKPropertyInspector (CoreData)
- (NSDictionary *)propertyNamesAndTypesForEntity:(NSEntityDescription *)entity
- (NSDictionary *)propertyNamesAndClassesForEntity:(NSEntityDescription *)entity
{
NSMutableDictionary *propertyNamesAndTypes = [_propertyNamesToTypesCache objectForKey:[entity name]];
if (propertyNamesAndTypes) {
@@ -44,7 +44,11 @@ RK_FIX_CATEGORY_BUG(RKPropertyInspector_CoreData)
for (NSString *name in [entity attributesByName]) {
NSAttributeDescription *attributeDescription = [[entity attributesByName] valueForKey:name];
if ([attributeDescription attributeValueClassName]) {
[propertyNamesAndTypes setValue:NSClassFromString([attributeDescription attributeValueClassName]) forKey:name];
Class cls = NSClassFromString([attributeDescription attributeValueClassName]);
if ([cls isSubclassOfClass:[NSNumber class]] && [attributeDescription attributeType] == NSBooleanAttributeType) {
cls = objc_getClass("NSCFBoolean") ?: objc_getClass("__NSCFBoolean") ?: cls;
}
[propertyNamesAndTypes setValue:cls forKey:name];
} else if ([attributeDescription attributeType] == NSTransformableAttributeType &&
![name isEqualToString:@"_mapkit_hasPanoramaID"]) {
@@ -53,12 +57,10 @@ RK_FIX_CATEGORY_BUG(RKPropertyInspector_CoreData)
const char *propertyName = [name cStringUsingEncoding:NSUTF8StringEncoding];
Class managedObjectClass = objc_getClass(className);
// property_getAttributes() returns everything we need to implement this...
// See: http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW5
objc_property_t prop = class_getProperty(managedObjectClass, propertyName);
NSString *attributeString = [NSString stringWithCString:property_getAttributes(prop) encoding:NSUTF8StringEncoding];
const char *destinationClassName = [[RKPropertyInspector propertyTypeFromAttributeString:attributeString] cStringUsingEncoding:NSUTF8StringEncoding];
Class destinationClass = objc_getClass(destinationClassName);
const char *attr = property_getAttributes(prop);
Class destinationClass = RKKeyValueCodingClassFromPropertyAttributes(attr);
if (destinationClass) {
[propertyNamesAndTypes setObject:destinationClass forKey:name];
}
@@ -81,9 +83,9 @@ RK_FIX_CATEGORY_BUG(RKPropertyInspector_CoreData)
return propertyNamesAndTypes;
}
- (Class)typeForProperty:(NSString *)propertyName ofEntity:(NSEntityDescription *)entity
- (Class)classForPropertyNamed:(NSString *)propertyName ofEntity:(NSEntityDescription *)entity
{
return [[self propertyNamesAndTypesForEntity:entity] valueForKey:propertyName];
return [[self propertyNamesAndClassesForEntity:entity] valueForKey:propertyName];
}
@end

View File

@@ -294,7 +294,7 @@ NSDate *RKDateFromStringWithFormatters(NSString *dateString, NSArray *formatters
- (Class)classForProperty:(NSString *)propertyName
{
return [[RKPropertyInspector sharedInspector] typeForProperty:propertyName ofClass:self.objectClass];
return [[RKPropertyInspector sharedInspector] classForPropertyNamed:propertyName ofClass:self.objectClass];
}
- (Class)classForKeyPath:(NSString *)keyPath
@@ -302,7 +302,7 @@ NSDate *RKDateFromStringWithFormatters(NSString *dateString, NSArray *formatters
NSArray *components = [keyPath componentsSeparatedByString:@"."];
Class propertyClass = self.objectClass;
for (NSString *property in components) {
propertyClass = [[RKPropertyInspector sharedInspector] typeForProperty:property ofClass:propertyClass];
propertyClass = [[RKPropertyInspector sharedInspector] classForPropertyNamed:property ofClass:propertyClass];
if (! propertyClass) break;
}

View File

@@ -68,3 +68,30 @@ BOOL RKObjectIsCollection(id object);
@return `YES` if the object is a collection containing only `NSManagedObject` derived objects.
*/
BOOL RKObjectIsCollectionContainingOnlyManagedObjects(id object);
/**
Returns an appropriate class to use for KVC access based on the Objective C runtime type encoding.
Objective C Runtime type encodings: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
KVC Scalar/Structure support: http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/KeyValueCoding/Articles/DataTypes.html#//apple_ref/doc/uid/20002171-BAJEAIEE
@param type An Objective C Runtime type encoding
@return The class name for the property type encoded in the given attribute string, an appropriate class for wrapping/unwrapping the primitive type, or `Nil` when no transformation is required or possible.
*/
Class RKKeyValueCodingClassForObjCType(const char *type);
/**
Returns an appropriate class to use for KVC access based on the output obtained via the `property_getAttributes` reflection API.
@param attributeString A c string containing encoding attribute information.
@return The class name for the property type encoded in the given attribute string, an appropriate class for wrapping/unwrapping the primitive type, or `Nil` when no transformation is required or possible.
*/
Class RKKeyValueCodingClassFromPropertyAttributes(const char *attr);
/**
Returns the name of a property when provided the name of a property obtained via the `property_getAttributes` reflection API.
@param attributeString A string object encoding attribute information.
@return The class name for the property type encoded in the given attribute string or `@"NULL"` if the property does not have an object type (the declared property is for a primitive type).
*/
NSString *RKPropertyTypeFromAttributeString(NSString *attributeString);

View File

@@ -71,3 +71,91 @@ BOOL RKObjectIsCollectionContainingOnlyManagedObjects(id object)
}
return YES;
}
Class RKKeyValueCodingClassForObjCType(const char *type)
{
if (type) {
// https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
switch (type[0]) {
case '@': {
char *openingQuoteLoc = strchr(type, '"');
if (openingQuoteLoc) {
char *closingQuoteLoc = strchr(openingQuoteLoc+1, '"');
if (closingQuoteLoc) {
size_t classNameStrLen = closingQuoteLoc-openingQuoteLoc;
char className[classNameStrLen];
memcpy(className, openingQuoteLoc+1, classNameStrLen-1);
// Null-terminate the array to stringify
className[classNameStrLen-1] = '\0';
return objc_getClass(className);
}
}
// If there is no quoted class type (id), it can be used as-is.
return Nil;
}
case 'c': // char
case 'C': // unsigned char
case 's': // short
case 'S': // unsigned short
case 'i': // int
case 'I': // unsigned int
case 'l': // long
case 'L': // unsigned long
case 'q': // long long
case 'Q': // unsigned long long
case 'f': // float
case 'd': // double
return [NSNumber class];
case 'B': // C++ bool or C99 _Bool
return objc_getClass("NSCFBoolean")
?: objc_getClass("__NSCFBoolean")
?: [NSNumber class];
case '{': // struct
case 'b': // bitfield
case '(': // union
return [NSValue class];
case '[': // c array
case '^': // pointer
case 'v': // void
case '*': // char *
case '#': // Class
case ':': // selector
case '?': // unknown type (function pointer, etc)
default:
break;
}
}
return Nil;
}
Class RKKeyValueCodingClassFromPropertyAttributes(const char *attr)
{
if (attr) {
const char *typeIdentifierLoc = strchr(attr, 'T');
if (typeIdentifierLoc) {
return RKKeyValueCodingClassForObjCType(typeIdentifierLoc+1);
}
}
return Nil;
}
NSString *RKPropertyTypeFromAttributeString(NSString *attributeString)
{
NSString *type = [NSString string];
NSScanner *typeScanner = [NSScanner scannerWithString:attributeString];
[typeScanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"@"] intoString:NULL];
// we are not dealing with an object
if ([typeScanner isAtEnd]) {
return @"NULL";
}
[typeScanner scanCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"@"] intoString:NULL];
// this gets the actual object type
[typeScanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\""] intoString:&type];
return type;
}

View File

@@ -60,18 +60,6 @@
@param objectClass The class to retrieve the property from.
@return A `Class` object specifying the type of the requested property.
*/
- (Class)typeForProperty:(NSString *)propertyName ofClass:(Class)objectClass;
///------------------------------------------------------
/// @name Retrieving the Properties and Types for a Class
///------------------------------------------------------
/**
Returns the name of a property when provided the name of a property obtained via the `property_getAttributes` reflection API.
@param attributeString A string object encoding attribute information.
@return The class name for the property type encoded in the given attribute string or `@"NULL"` if the property does not have an object type (the declared property is for a primitive type).
*/
+ (NSString *)propertyTypeFromAttributeString:(NSString *)attributeString;
- (Class)classForPropertyNamed:(NSString *)propertyName ofClass:(Class)objectClass;
@end

View File

@@ -18,9 +18,10 @@
// limitations under the License.
//
#import <objc/message.h>
#import <objc/runtime.h>
#import "RKPropertyInspector.h"
#import "RKLog.h"
#import "RKObjectUtilities.h"
// Set Logging Component
#undef RKLogComponent
@@ -49,23 +50,6 @@
return self;
}
+ (NSString *)propertyTypeFromAttributeString:(NSString *)attributeString
{
NSString *type = [NSString string];
NSScanner *typeScanner = [NSScanner scannerWithString:attributeString];
[typeScanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"@"] intoString:NULL];
// we are not dealing with an object
if ([typeScanner isAtEnd]) {
return @"NULL";
}
[typeScanner scanCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"@"] intoString:NULL];
// this gets the actual object type
[typeScanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\""] intoString:&type];
return type;
}
- (NSDictionary *)propertyNamesAndTypesForClass:(Class)theClass
{
NSMutableDictionary *propertyNames = [_propertyNamesToTypesCache objectForKey:theClass];
@@ -78,24 +62,24 @@
Class currentClass = theClass;
while (currentClass != nil) {
// Get the raw list of properties
unsigned int outCount;
unsigned int outCount = 0;
objc_property_t *propList = class_copyPropertyList(currentClass, &outCount);
// Collect the property names
int i;
NSString *propName;
for (i = 0; i < outCount; i++) {
// property_getAttributes() returns everything we need to implement this...
// See: http://developer.apple.com/mac/library/DOCUMENTATION/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html#//apple_ref/doc/uid/TP40008048-CH101-SW5
for (typeof(outCount) i = 0; i < outCount; i++) {
objc_property_t *prop = propList + i;
NSString *attributeString = [NSString stringWithCString:property_getAttributes(*prop) encoding:NSUTF8StringEncoding];
propName = [NSString stringWithCString:property_getName(*prop) encoding:NSUTF8StringEncoding];
const char *propName = property_getName(*prop);
if (![propName isEqualToString:@"_mapkit_hasPanoramaID"]) {
const char *className = [[RKPropertyInspector propertyTypeFromAttributeString:attributeString] cStringUsingEncoding:NSUTF8StringEncoding];
Class aClass = objc_getClass(className);
if (aClass) {
[propertyNames setObject:aClass forKey:propName];
if (strcmp(propName, "_mapkit_hasPanoramaID") != 0) {
const char *attr = property_getAttributes(*prop);
if (attr) {
Class aClass = RKKeyValueCodingClassFromPropertyAttributes(attr);
if (aClass) {
NSString *propNameObj = [[NSString alloc] initWithCString:propName encoding:NSUTF8StringEncoding];
if (propNameObj) {
[propertyNames setObject:aClass forKey:propNameObj];
}
}
}
}
}
@@ -109,7 +93,7 @@
return propertyNames;
}
- (Class)typeForProperty:(NSString *)propertyName ofClass:(Class)objectClass
- (Class)classForPropertyNamed:(NSString *)propertyName ofClass:(Class)objectClass
{
NSDictionary *dictionary = [self propertyNamesAndTypesForClass:objectClass];
return [dictionary objectForKey:propertyName];