iOS运行时属性的读取

2017-08-21  本文已影响30人  anjohnlv

我们想操作一个类的属性。
在编译时可以通过.或者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、方法签名(不完善)

首先手动合成settergetter方法

-(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];

在运行时来操作的好处是更加的灵活,不用限定于具体的类。
在封装工具的时候更加高效。

上一篇下一篇

猜你喜欢

热点阅读