我們想操作一個類的屬性。
在編譯時可以通過.或者setter getter方法,很方便的進行讀取操作。
在這里,我主要討論運行時的屬性讀取。
首先獲取到指定類所有的自定義屬性名。
-(NSArray *)allPropertyName {
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList([self class], &propertyCount);
NSMutableArray *allPropertyName = [NSMutableArray new];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
[allPropertyName addObject:propertyName];
}
free(properties);
return allPropertyName;
}
接下來,我想到了兩種方法:
1、方法簽名(不完善)
首先手動合成setter和getter方法
-(SEL)getterWithPropertyName:(NSString *)propertyName {
return NSSelectorFromString(propertyName);
}
-(SEL)setterWithPropertyName:(NSString *)propertyName {
NSString *firstLetter = [propertyName substringToIndex:1];
NSString *upFirstLetter = [firstLetter uppercaseString];
NSString *setter = [NSString stringWithFormat:@"set%@%@:",upFirstLetter,[propertyName substringFromIndex:1]];
return NSSelectorFromString(setter);
}
再通過方法簽名獲得getter方法的返回值
-(id)propertyValue:(SEL)getSel {
if ([self respondsToSelector:getSel]) {
//獲得簽名
NSMethodSignature *signature = [self methodSignatureForSelector:getSel];
//從簽名獲得調用對象
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
//設置target
[invocation setTarget:self];
//設置selector
[invocation setSelector:getSel];
//接收返回的值
id returnValue = nil;
//調用
[invocation invoke];
//獲得返回值類型
const char *returnType = signature.methodReturnType;
//如果沒有返回值,也就是消息聲明為void,那么returnValue=nil
if( !strcmp(returnType, @encode(void)) ){
returnValue = nil;
} else if (!strcmp(returnType, @encode(id))){
//返回值為對象,那么為變量賦值
[invocation getReturnValue:&returnValue];
} else {
//返回值為基本數據類型NSInteger BOOL int float等
//返回值長度
NSInteger length = [signature methodReturnLength];
//根據長度申請內存
void *buffer = (void *)malloc(length);
if(!strcmp(returnType, @encode(BOOL))) {
returnValue = [NSNumber numberWithBool:*((BOOL*)buffer)];
} else if(!strcmp(returnType, @encode(NSInteger))){
returnValue = [NSNumber numberWithInteger:*((NSInteger*)buffer)];
} else if(!strcmp(returnType, @encode(int))) {
returnValue = [NSNumber numberWithInt:*((BOOL*)buffer)];
}else{
returnValue = [NSValue valueWithBytes:buffer objCType:returnType];
}
}
return returnValue;
}
return nil;
}
最后調用setter方法,將返回值進行賦值
SEL setSel = [self setterWithPropertyName:propertyName];
SEL getSel = [self getterWithPropertyName:propertyName];
id obj = [self propertyValue:getSel];
if (obj) {
[copyObject performSelector:setSel withObject:obj];
}
這種方法之所以說不完善,是因為經過反復測試,id類型的屬性基本正常,但基本數據類型的返回值始終不正確。也希望有大佬可以解答留下的問題。
2、KVC
這種方式很簡單,就不多解釋了。
NSValue *value = [self valueForKey:propertyName];
[copyObject setValue:value forKey:propertyName];
在運行時來操作的好處是更加的靈活,不用限定于具體的類。
在封裝工具的時候更加高效。