我们想操作一个类的属性。
在编译时可以通过.
或者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];
在运行时来操作的好处是更加的灵活,不用限定于具体的类。
在封装工具的时候更加高效。