Skip to content

Instantly share code, notes, and snippets.

@nielsbot
Created April 12, 2012 20:32
Show Gist options
  • Save nielsbot/2370784 to your computer and use it in GitHub Desktop.
Save nielsbot/2370784 to your computer and use it in GitHub Desktop.
PropertyContainer
//
// 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
//
// 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