Created
February 9, 2011 15:14
-
-
Save masakih/818616 to your computer and use it in GitHub Desktop.
automatic dynamic generate property body on legacy runtime.
This file contains 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
// | |
// DynamicPropertyGenerator.m | |
// DynamicPropertyGenerator | |
// | |
// Created by Hori,Masaki on 11/02/09. | |
// Copyright 2011 masakih. All rights reserved. | |
// | |
#import "DynamicPropertyGenerator.h" | |
#import <objc/runtime.h> | |
#import "PropertyBodyGenerator.h" | |
@implementation NSObject(DynamicPropertyGenerator) | |
+ (void)initialize | |
{ | |
if(self == [NSObject class]) { | |
Method originalAlloc = class_getClassMethod([self class], @selector(allocWithZone:)); | |
Method replaceAlloc = class_getClassMethod([self class], @selector(allocAndHackWithZone:)); | |
method_exchangeImplementations(originalAlloc, replaceAlloc); | |
} | |
} | |
+ (id)allocAndHackWithZone:(NSZone *)zone | |
{ | |
id result = [self allocAndHackWithZone:zone]; | |
genarateWithClass(self); | |
return result; | |
} | |
@end |
This file contains 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
// | |
// PropertyBodyGenerator.h | |
// DynamicPropertyGenerator | |
// | |
// Created by Hori,Masaki on 11/02/10. | |
// Copyright 2011 masakih. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
void genarateWithClass(Class class); |
This file contains 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
// | |
// PropertyDescription.h | |
// DynamicPropertyGenerator | |
// | |
// Created by Hori,Masaki on 11/02/10. | |
// Copyright 2011 masakih. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
#import <objc/runtime.h> | |
@interface PropertyDescription : NSObject | |
{ | |
char *name; | |
char *getter; | |
char *setter; | |
char *type; | |
struct { | |
char isReadOnly : 1; | |
char dynamic : 1; | |
} _attr; | |
objc_AssociationPolicy policy; | |
IMP setterImp; | |
IMP getterImp; | |
} | |
+ (id)propertyDescriptionWithClass:(Class)class getter:(SEL)getterSelector; | |
+ (id)propertyDescriptionWithClass:(Class)class setter:(SEL)setterSelector; | |
@end |
This file contains 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
// | |
// PropertyDescription.m | |
// DynamicPropertyGenerator | |
// | |
// Created by Hori,Masaki on 11/02/10. | |
// Copyright 2011 masakih. All rights reserved. | |
// | |
#import "PropertyDescription.h" | |
#import "PropertyBodyGenerator.h" | |
static NSMutableDictionary *sClassStack = nil; | |
@interface PropertyDescription (PropertyGenerator) | |
+ (id)descriptionWithAttributes:(const char *)inAttributes; | |
- (void)setPropertyAttributes:(const char *)attributes; | |
- (void)setName:(const char *)name; | |
- (const char *)name; | |
- (void)setSetter:(const char *)setter; | |
- (const char *)setter; | |
- (void)setGetter:(const char *)getter; | |
- (const char *)getter; | |
- (void)setType:(const char *)type; | |
- (void)setReadOnly:(BOOL)flag; | |
- (BOOL)isReadOnly; | |
- (void)setDynamic:(BOOL)flag; | |
- (BOOL)dynamic; | |
- (void)setRetain:(BOOL)flag; | |
- (void)setCopy:(BOOL)flag; | |
- (void)setNonatomic:(BOOL)flag; | |
- (objc_AssociationPolicy)policy; | |
@end | |
#pragma mark- | |
#pragma mark#### getter/setter implementation #### | |
id objectGetterTemplateIMP(id self, SEL _cmd) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] getter:_cmd]; | |
id result = objc_getAssociatedObject(self, (void *)[desc name]); | |
return result; | |
} | |
void objectSetterTemplateIMP(id self, SEL _cmd, id obj) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] setter:_cmd]; | |
objc_setAssociatedObject(self, (void *)[desc name], obj, [desc policy]); | |
} | |
SInt32 int32GetterTemplateIMP(id self, SEL _cmd) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] getter:_cmd]; | |
id result = objc_getAssociatedObject(self, (void *)[desc name]); | |
return [result integerValue]; | |
} | |
void int32SetterTemplateIMP(id self, SEL _cmd, SInt32 value) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] setter:_cmd]; | |
id obj = [NSNumber numberWithLong:value]; | |
objc_setAssociatedObject(self, (void *)[desc name], obj, OBJC_ASSOCIATION_RETAIN); | |
} | |
SInt64 int64GetterTemplateIMP(id self, SEL _cmd) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] getter:_cmd]; | |
id result = objc_getAssociatedObject(self, (void *)[desc name]); | |
return [result longLongValue]; | |
} | |
void int64SetterTemplateIMP(id self, SEL _cmd, SInt64 value) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] setter:_cmd]; | |
id obj = [NSNumber numberWithLongLong:value]; | |
objc_setAssociatedObject(self, (void *)[desc name], obj, OBJC_ASSOCIATION_RETAIN); | |
} | |
float floatGetterTemplateIMP(id self, SEL _cmd) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] getter:_cmd]; | |
id result = objc_getAssociatedObject(self, (void *)[desc name]); | |
return [result floatValue]; | |
} | |
void floatSetterTemplateIMP(id self, SEL _cmd, float value) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] setter:_cmd]; | |
id obj = [NSNumber numberWithFloat:value]; | |
objc_setAssociatedObject(self, (void *)[desc name], obj, OBJC_ASSOCIATION_RETAIN); | |
} | |
double doubleGetterTemplateIMP(id self, SEL _cmd) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] getter:_cmd]; | |
id result = objc_getAssociatedObject(self, (void *)[desc name]); | |
return [result doubleValue]; | |
} | |
void doubleSetterTemplateIMP(id self, SEL _cmd, double value) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] setter:_cmd]; | |
id obj = [NSNumber numberWithDouble:value]; | |
objc_setAssociatedObject(self, (void *)[desc name], obj, OBJC_ASSOCIATION_RETAIN); | |
} | |
NSSize sizeGetterTemplateIMP(id self, SEL _cmd) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] getter:_cmd]; | |
id result = objc_getAssociatedObject(self, (void *)[desc name]); | |
return [result sizeValue]; | |
} | |
void sizeSetterTemplateIMP(id self, SEL _cmd, NSSize value) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] setter:_cmd]; | |
id obj = [NSValue valueWithSize:value]; | |
objc_setAssociatedObject(self, (void *)[desc name], obj, OBJC_ASSOCIATION_RETAIN); | |
} | |
NSPoint pointGetterTemplateIMP(id self, SEL _cmd) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] getter:_cmd]; | |
id result = objc_getAssociatedObject(self, (void *)[desc name]); | |
return [result pointValue]; | |
} | |
void pointSetterTemplateIMP(id self, SEL _cmd, NSPoint value) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] setter:_cmd]; | |
id obj = [NSValue valueWithPoint:value]; | |
objc_setAssociatedObject(self, (void *)[desc name], obj, OBJC_ASSOCIATION_RETAIN); | |
} | |
NSRect rectGetterTemplateIMP(id self, SEL _cmd) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] getter:_cmd]; | |
id result = objc_getAssociatedObject(self, (void *)[desc name]); | |
return [result rectValue]; | |
} | |
void rectSetterTemplateIMP(id self, SEL _cmd, NSRect value) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] setter:_cmd]; | |
id obj = [NSValue valueWithRect:value]; | |
objc_setAssociatedObject(self, (void *)[desc name], obj, OBJC_ASSOCIATION_RETAIN); | |
} | |
NSRange rangeGetterTemplateIMP(id self, SEL _cmd) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] getter:_cmd]; | |
id result = objc_getAssociatedObject(self, (void *)[desc name]); | |
return [result rangeValue]; | |
} | |
void rangeSetterTemplateIMP(id self, SEL _cmd, NSRange value) | |
{ | |
PropertyDescription *desc = [PropertyDescription propertyDescriptionWithClass:[self class] setter:_cmd]; | |
id obj = [NSValue valueWithRange:value]; | |
objc_setAssociatedObject(self, (void *)[desc name], obj, OBJC_ASSOCIATION_RETAIN); | |
} | |
#pragma mark- | |
@implementation PropertyDescription | |
+ (id)propertyDescriptionWithClass:(Class)class getter:(SEL)getterSelector | |
{ | |
id props = [sClassStack objectForKey:NSStringFromClass(class)]; | |
if(!props) return nil; | |
const char *getter = sel_getName(getterSelector); | |
for(PropertyDescription *desc in props) { | |
if(0 == strcmp([desc getter], getter)) return desc; | |
} | |
return nil; | |
} | |
+ (id)propertyDescriptionWithClass:(Class)class setter:(SEL)setterSelector | |
{ | |
id props = [sClassStack objectForKey:NSStringFromClass(class)]; | |
if(!props) return nil; | |
const char *setter = sel_getName(setterSelector); | |
for(PropertyDescription *desc in props) { | |
if(0 == strcmp([desc setter], setter)) return desc; | |
} | |
return nil; | |
} | |
+ (id)descriptionWithAttributes:(const char *)inAttributes | |
{ | |
IMP getterImp = NULL; | |
IMP setterImp = NULL; | |
BOOL structType = NO; | |
const char *inType = &inAttributes[1]; | |
switch(inType[0]) { | |
case _C_ID: | |
getterImp = (IMP)objectGetterTemplateIMP; | |
setterImp = (IMP)objectSetterTemplateIMP; | |
break; | |
case _C_CHR: | |
case _C_UCHR: | |
case _C_SHT: | |
case _C_USHT: | |
case _C_INT: | |
case _C_UINT: | |
case _C_LNG: | |
case _C_ULNG: | |
case _C_BOOL: | |
case _C_PTR: | |
case _C_CHARPTR: | |
getterImp = (IMP)int32GetterTemplateIMP; | |
setterImp = (IMP)int32SetterTemplateIMP; | |
break; | |
case _C_LNG_LNG: | |
case _C_ULNG_LNG: | |
getterImp = (IMP)int64GetterTemplateIMP; | |
setterImp = (IMP)int64SetterTemplateIMP; | |
break; | |
case _C_FLT: | |
getterImp = (IMP)floatGetterTemplateIMP; | |
setterImp = (IMP)floatSetterTemplateIMP; | |
break; | |
case _C_DBL: | |
getterImp = (IMP)doubleGetterTemplateIMP; | |
setterImp = (IMP)doubleSetterTemplateIMP; | |
break; | |
case _C_STRUCT_B: | |
structType = YES; | |
break; | |
default: | |
goto end; | |
return nil; | |
} | |
do { | |
if(structType) { | |
char *f; | |
f=strstr(inType, "{_NSSize="); | |
if(f==inType) { | |
getterImp = (IMP)sizeGetterTemplateIMP; | |
setterImp = (IMP)sizeSetterTemplateIMP; | |
break; | |
} | |
f=strstr(inType, "{_NSPoint="); | |
if(f==inType) { | |
getterImp = (IMP)pointGetterTemplateIMP; | |
setterImp = (IMP)pointSetterTemplateIMP; | |
break; | |
} | |
f=strstr(inType, "{_NSRect="); | |
if(f==inType) { | |
getterImp = (IMP)rectGetterTemplateIMP; | |
setterImp = (IMP)rectSetterTemplateIMP; | |
break; | |
} | |
f=strstr(inType, "{_NSRange="); | |
if(f==inType) { | |
getterImp = (IMP)rangeGetterTemplateIMP; | |
setterImp = (IMP)rangeSetterTemplateIMP; | |
break; | |
} | |
f=strstr(inType, "{CGSize="); | |
if(f==inType) { | |
getterImp = (IMP)sizeGetterTemplateIMP; | |
setterImp = (IMP)sizeSetterTemplateIMP; | |
break; | |
} | |
f=strstr(inType, "{CGPoint="); | |
if(f==inType) { | |
getterImp = (IMP)pointGetterTemplateIMP; | |
setterImp = (IMP)pointSetterTemplateIMP; | |
break; | |
} | |
f=strstr(inType, "{CGRect="); | |
if(f==inType) { | |
getterImp = (IMP)rectGetterTemplateIMP; | |
setterImp = (IMP)rectSetterTemplateIMP; | |
break; | |
} | |
f=strstr(inType, "{CGRange="); | |
if(f==inType) { | |
getterImp = (IMP)rangeGetterTemplateIMP; | |
setterImp = (IMP)rangeSetterTemplateIMP; | |
break; | |
} | |
} | |
} while (NO); | |
if(getterImp) { | |
PropertyDescription *result = [[[PropertyDescription alloc] init] autorelease]; | |
[result setPropertyAttributes:inAttributes]; | |
result->getterImp = getterImp; | |
result->setterImp = setterImp; | |
return result; | |
} | |
end: | |
fprintf(stderr, "PropertyDescription: %s dose not support property type.\n", inAttributes); | |
return nil; | |
} | |
- (void)dealloc | |
{ | |
free(name); | |
free(getter); | |
free(setter); | |
free(type); | |
[super dealloc]; | |
} | |
- (void)generateAccessorForClass:(Class)class | |
{ | |
const char *getterSelName = [self getter]; | |
SEL getterSel = sel_registerName(getterSelName); | |
char *getterImpType = NULL; | |
asprintf(&getterImpType, "%s@:", type); | |
BOOL ok = class_addMethod(class, getterSel, getterImp, getterImpType); | |
free(getterImpType); | |
if(!ok) { | |
fprintf(stderr, "Could not add getter method!\n"); | |
} | |
if(![self isReadOnly]) { | |
const char *setterSelName = [self setter]; | |
SEL setterSel = sel_registerName(setterSelName); | |
char *setterImpType = NULL; | |
asprintf(&setterImpType, "v@:%s", type); | |
ok = class_addMethod(class, setterSel, setterImp, setterImpType); | |
free(setterImpType); | |
if(!ok) { | |
fprintf(stderr, "Could not add setter method!\n"); | |
} | |
} | |
} | |
- (void)setName:(const char *)inName | |
{ | |
if(!inName) return; | |
asprintf(&name, "%s", inName); | |
} | |
- (const char *)name | |
{ | |
return name; | |
} | |
- (void)setSetter:(const char *)inSetter | |
{ | |
if(!inSetter) return; | |
asprintf(&setter, "%s", inSetter); | |
} | |
- (const char *)setter | |
{ | |
if(!setter && name) { | |
char *se = NULL; | |
asprintf(&se, "set%s:", name); | |
se[3] = toupper(se[3]); | |
[self setSetter:se]; | |
free(se); | |
} | |
return setter; | |
} | |
- (void)setGetter:(const char *)inGetter | |
{ | |
if(!inGetter) return; | |
asprintf(&getter, "%s", inGetter); | |
} | |
- (const char *)getter | |
{ | |
if(!getter && name) { | |
[self setGetter:name]; | |
} | |
return getter; | |
} | |
- (void)setType:(const char *)inType | |
{ | |
if(!inType) return; | |
if(inType[0] == '@') inType = "@"; | |
asprintf(&type, "%s", inType); | |
} | |
- (void)setRetain:(BOOL)flag | |
{ | |
if(flag) { | |
policy |= 01; | |
} else { | |
fprintf(stderr, "%s arg NO is not supported\n", __PRETTY_FUNCTION__); | |
} | |
} | |
- (void)setCopy:(BOOL)flag | |
{ | |
if(flag) { | |
policy |= 03; | |
} else { | |
fprintf(stderr, "%s arg NO is not supported\n", __PRETTY_FUNCTION__); | |
} | |
} | |
- (void)setNonatomic:(BOOL)flag | |
{ | |
if(flag) { | |
policy |= 01400; | |
} else { | |
fprintf(stderr, "%s arg NO is not supported\n", __PRETTY_FUNCTION__); | |
} | |
} | |
- (void)setReadOnly:(BOOL)flag | |
{ | |
_attr.isReadOnly = flag ? 1 : 0; | |
} | |
- (BOOL)isReadOnly | |
{ | |
return _attr.isReadOnly; | |
} | |
- (void)setDynamic:(BOOL)flag | |
{ | |
_attr.dynamic = flag ? 1 : 0; | |
} | |
- (BOOL)dynamic | |
{ | |
return _attr.dynamic; | |
} | |
- (objc_AssociationPolicy)policy | |
{ | |
return policy; | |
} | |
- (void)setPropertyAttributes:(const char *)attributes | |
{ | |
char *attrsCp = NULL; | |
asprintf(&attrsCp, "%s", attributes); | |
char *attr = NULL; | |
attr = strtok(attrsCp, ","); | |
do { | |
switch(attr[0]) { | |
case 'T': | |
// type | |
[self setType:&attr[1]]; | |
break; | |
case 'S': | |
// setter | |
[self setSetter:&attr[1]]; | |
break; | |
case 'G': | |
// getter | |
[self setGetter:&attr[1]]; | |
break; | |
case 'V': | |
// variable | |
break; | |
case '&': | |
// retain | |
[self setRetain:YES]; | |
break; | |
case 'C': | |
// copy | |
[self setCopy:YES]; | |
break; | |
case 'N': | |
// nonatomic | |
[self setNonatomic:YES]; | |
break; | |
case 'R': | |
// readonly | |
[self setReadOnly:YES]; | |
break; | |
case 'D': | |
// dynamic check | |
[self setDynamic:YES]; | |
break; | |
case 'P': | |
// Garbage Collection | |
break; | |
default: | |
fprintf(stderr, "Unknown type of property attribute. (%s)\n", attr); | |
exit(-1); | |
break; | |
} | |
attr = strtok(NULL, ","); | |
} while (attr); | |
free(attrsCp); | |
} | |
@end | |
#pragma mark- | |
void genarateWithClass(Class class) | |
{ | |
if(!sClassStack) { | |
sClassStack = [[NSMutableDictionary alloc] init]; | |
} | |
unsigned int count = 0; | |
objc_property_t *props = class_copyPropertyList(class, &count); | |
if(count == 0) return; | |
if([sClassStack objectForKey:NSStringFromClass(class)]) return; | |
NSMutableArray *classProp = [NSMutableArray array]; | |
[sClassStack setObject:classProp forKey:NSStringFromClass(class)]; | |
for(unsigned int i = 0; i < count; i++) { | |
const char *attrs = property_getAttributes(props[i]); | |
PropertyDescription *desc = [PropertyDescription descriptionWithAttributes:attrs]; | |
if(!desc) { | |
continue; | |
} | |
const char *name = property_getName(props[i]); | |
[desc setName:name]; | |
[desc setPropertyAttributes:attrs]; | |
if(![desc dynamic]) continue; | |
[desc generateAccessorForClass:class]; | |
[classProp addObject:desc]; | |
} | |
free(props); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment