iOS運行時屬性的讀取

我們想操作一個類的屬性。
在編譯時可以通過.或者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];

在運行時來操作的好處是更加的靈活,不用限定于具體的類。
在封裝工具的時候更加高效。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容