美文网首页
iOS运行时属性的读取

iOS运行时属性的读取

作者: anjohnlv | 来源:发表于2017-08-21 13:21 被阅读30次

    我们想操作一个类的属性。
    在编译时可以通过.或者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];
    

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

    相关文章

      网友评论

          本文标题:iOS运行时属性的读取

          本文链接:https://www.haomeiwen.com/subject/fspcdxtx.html