Created
April 12, 2012 20:32
-
-
Save nielsbot/2370784 to your computer and use it in GitHub Desktop.
PropertyContainer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// PropertyContainer.h | |
// | |
// Created by Niels Gabel on 12/14/11. | |
// Copyright (c) 2011 DoubleDutch Inc. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
@interface PropertyContainer : NSObject | |
@property ( nonatomic, retain, readonly ) NSMutableDictionary * properties ; | |
-(id)defaultValueForKey:(NSString*)key ; // default returns nil | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// PropertyContainer.m | |
// | |
// Created by Niels Gabel on 12/14/11. | |
// Copyright (c) 2011 DoubleDutch Inc. All rights reserved. | |
// | |
#import "PropertyContainer.h" | |
#import <objc/runtime.h> | |
@interface PropertyContainer () | |
@property ( nonatomic, retain ) NSMutableDictionary * properties ; | |
@end | |
@implementation PropertyContainer | |
@synthesize properties = _properties ; | |
+ (BOOL)resolveInstanceMethod:(SEL)sel | |
{ | |
if ( ![ super resolveInstanceMethod:sel ] ) | |
{ | |
NSString * selectorName = NSStringFromSelector( sel ) ; | |
BOOL isSetter = [ selectorName hasPrefix:@"set" ] && [ selectorName hasSuffix:@":" ] && ( selectorName.length > 4 ) ; | |
NSString * propertyName = nil ; | |
if ( isSetter ) | |
{ | |
propertyName = [[[ selectorName substringWithRange:(NSRange){ 3, 1 } ] lowercaseString ] stringByAppendingString:[ selectorName substringWithRange:(NSRange){ 4, selectorName.length - 5 } ] ]; | |
} | |
else | |
{ | |
if ( [selectorName hasPrefix:@"get" ] ) | |
{ | |
selectorName = [ selectorName substringFromIndex:3 ] ; | |
} | |
propertyName = selectorName ; | |
} | |
char * propType = propertyName ? property_copyAttributeValue( class_getProperty( self, [ propertyName UTF8String ] ), "T" ) : NULL ; | |
if ( propType ) | |
{ | |
NSString * propTypeString = [ NSString stringWithUTF8String:propType ] ; | |
id (^block)() = NULL ; | |
if ( isSetter ) | |
{ | |
if ( propType[0] == @encode( id )[0] ) | |
{ | |
block = (id(^)())^( PropertyContainer * self, id argument) { | |
if ( argument ) | |
{ | |
[ self.properties setObject:argument forKey:propertyName ] ; | |
} | |
else | |
{ | |
[ self.properties removeObjectForKey:propertyName ] ; | |
} | |
} ; | |
} | |
else if ( propType[0] == @encode( struct {} )[0] | |
|| propType[0] == @encode( void* )[0] ) | |
{ | |
// handle struct types up to 32 bytes: | |
typedef struct { uint8_t storage[32] ; } someStruct ; | |
NSUInteger size = 0 ; | |
NSGetSizeAndAlignment( propType, & size, NULL ) ; | |
DebugAssert( size <= sizeof( someStruct ) ) ; | |
block = (id(^)())^(PropertyContainer * self, someStruct arg ) { | |
[ self.properties setObject:[ NSValue valueWithBytes:& arg objCType:[ propTypeString UTF8String ] ] | |
forKey:propertyName ] ; | |
} ; | |
} | |
else if ( strcmp( propType, @encode( BOOL ) ) == 0 ) | |
{ | |
block = (id(^)())^(PropertyContainer * self, BOOL b ) { | |
[ self.properties setObject:[ NSNumber numberWithBool:b ] forKey:propertyName ] ; | |
} ; | |
} | |
else if ( strcmp( propType, @encode( NSUInteger ) ) == 0 ) | |
{ | |
block = (id(^)())^(PropertyContainer * self, NSUInteger i ) { | |
[ self.properties setObject:[ NSNumber numberWithUnsignedInteger:i ] forKey:propertyName ] ; | |
} ; | |
} | |
else if ( strcmp( propType, @encode( NSInteger ) ) == 0 ) | |
{ | |
block = (id(^)())^(PropertyContainer * self, NSInteger i ) { | |
[ self.properties setObject:[ NSNumber numberWithInteger:i ] forKey:propertyName ] ; | |
} ; | |
} | |
} | |
else | |
{ | |
if ( propType[0] == @encode( id )[0] ) | |
{ | |
block = (id(^)())^(PropertyContainer * self) { | |
return [ self/*.properties*/ valueForKey:propertyName ] ; | |
} ; | |
} | |
else if ( strcmp( propType, @encode( float ) ) == 0 ) | |
{ | |
block = (id(^)())^(PropertyContainer * self ) { | |
return [ [ self/*.properties*/ valueForKey:propertyName ] floatValue ] ; | |
} ; | |
} | |
else if ( strcmp( propType, @encode( CGRect ) ) == 0 ) | |
{ | |
block = (id(^)())^(PropertyContainer * self ) { | |
return [ [ self/*.properties*/ valueForKey:propertyName ] CGRectValue ] ; | |
} ; | |
} | |
else if ( strcmp( propType, @encode( CGPoint ) ) == 0 ) | |
{ | |
block = (id(^)())^(PropertyContainer * self ) { | |
return [ [ self/*.properties*/ valueForKey:propertyName ] CGPointValue ] ; | |
} ; | |
} | |
else if ( strcmp( propType, @encode( BOOL ) ) == 0 ) | |
{ | |
block = (id(^)())^(PropertyContainer * self ) { | |
return (BOOL)[ [ self/*.properties*/ valueForKey:propertyName ] boolValue ] ; | |
} ; | |
} | |
else if ( strcmp( propType, @encode( NSUInteger ) ) == 0 ) | |
{ | |
block = (id(^)())^(PropertyContainer * self ) { | |
return (NSUInteger)[ [ self/*.properties*/ valueForKey:propertyName ] unsignedIntegerValue ] ; | |
} ; | |
} | |
else if ( strcmp( propType, @encode( NSInteger ) ) == 0 ) | |
{ | |
block = (id(^)())^(PropertyContainer * self ) { | |
return (NSInteger)[ [ self/*.properties*/ valueForKey:propertyName ] integerValue ] ; | |
} ; | |
} | |
else if ( strcmp( propType, @encode( int ) ) == 0 ) | |
{ | |
block = (id(^)())^(PropertyContainer * self ) { | |
return (int)[ [ self/*.properties*/ valueForKey:propertyName ] intValue ] ; | |
} ; | |
} | |
} | |
if ( block ) | |
{ | |
const char * types = [ [ NSString stringWithFormat:isSetter ? @"v@:%c" : @"%c@:", propType[0] ] UTF8String ] ; | |
IMP imp = imp_implementationWithBlock( (__bridge void *)[ block copy ] ) ; | |
class_addMethod( self, sel, imp, types ) ; | |
return YES ; | |
} | |
else | |
{ | |
DebugLog(@"PropertyContainer can't make %s! propertyName=%@ type=%s\n", | |
isSetter ? "setter" : "getter", | |
propertyName, | |
propType ) ; | |
} | |
free((void*)propType); | |
} | |
} | |
return NO ; | |
} | |
-(void)setValue:(id)value forUndefinedKey:(NSString *)key | |
{ | |
} | |
-(id)init | |
{ | |
if (( self = [ super init ] )) | |
{ | |
_properties = [ NSMutableDictionary dictionary ] ; | |
} | |
return self ; | |
} | |
-(NSString*)description | |
{ | |
NSMutableString * result = [ NSMutableString string ] ; | |
__block NSString * separator = @"" ; | |
[ self.properties enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { | |
[ result appendFormat:@"%@%@=%@", separator, key, obj ] ; | |
separator = @"," ; | |
}]; | |
result = [ NSString stringWithFormat:@"<%@<%p>>{%@}", [ self class ], self, result ] ; | |
return result ; | |
} | |
-(id)valueForKey:(NSString *)key | |
{ | |
id result = [ self.properties valueForKey:key ] ; | |
if ( !result ) | |
{ | |
result = [ self defaultValueForKey:key ] ; | |
} | |
return result ; | |
} | |
-(id)defaultValueForKey:(NSString *)key | |
{ | |
// DebugLog(@"%@<%p> Returning 'nil' default value for key %@\n", [ self class ], self, key); | |
return nil ; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment