Skip to content

Instantly share code, notes, and snippets.

@masakih
Created February 9, 2011 15:14
Show Gist options
  • Save masakih/818616 to your computer and use it in GitHub Desktop.
Save masakih/818616 to your computer and use it in GitHub Desktop.
automatic dynamic generate property body on legacy runtime.
//
// 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
//
// 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);
//
// 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
//
// 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