利用RunTime做一點點代碼優(yōu)化記錄

現(xiàn)狀

業(yè)務(wù)這邊之前有需求各種config,從后端拿到數(shù)據(jù),一個字典挨個解析。
最早的寫法如下:

...
    NSNumber* xxxx = [infoDic objectForKey:@"xxxx"];
    if (xxxx != nil && [xxxx isKindOfClass:[NSNumber class]]) {// or [NSString class]
        int nRateDialog = [xxxx intValue];
        if (nRateDialog == 1) {
            infoModel.xxxx = YES;
        }else{
            infoModel.xxxx = NO;
        }
    }
** 很多類似的寫法
...

代碼臃腫不說,很丑。

但是,因為上古版本的原因,類中定義的數(shù)據(jù)類型也是隨心所欲。

int NSInteger NSString NSNumber BOOL float CGFloat

幾乎包含了所有的能用到的類型...
再加上這邊要給QA做一個開關(guān)的需求,本地隨便修改這些config。所有有了這篇記錄。
當(dāng)然如果你本身代碼很規(guī)范,直接用Mantle或者其他的工具解析就完事了。我這邊因為是上古代碼的關(guān)系,不可能直接修改改動太大,所以才出此下策。

大概思路

  1. 后端或者本地緩存拿到數(shù)據(jù)。
  2. 如果是QA環(huán)境需要檢查本地的debug數(shù)據(jù),看是否設(shè)置過本地的config,如果設(shè)置過則需要將debug數(shù)據(jù)更新上去。
  3. 通過便利拿到的數(shù)據(jù)通過key值通過runtimeobject_setIvar一一對應(yīng)的設(shè)給對應(yīng)的Model。

難點一

  1. 代碼里面的屬性名稱跟后端的Key對不上。
    比如,后端的key是TestKey,但是代碼里面的屬性名稱是1111。(只是比喻)
  2. 有一些config在設(shè)計的時候不是按照一個規(guī)范來的。需要對這個config做一個特殊的處理。

難點解決

  1. 照著Mantle的模式寫了一個方法返回一個字典。對應(yīng)的Key、Value分別是后端的Key以及本地的屬性名稱。
  2. 另外一個方法,返回的是需要特殊處理的key。

這樣寫的好處是便于后續(xù)擴展。
如果真的需要兩端不相同,只需在1方法中加上對應(yīng)的Key、Value就行。如果需要特殊處理只需在2加對應(yīng)的值就行。并且,后續(xù)添加config只需.h 方法中新增就行,無需多加代碼,提高效率。

難點二

在寫代碼過程中,發(fā)現(xiàn)runtime的方法object_setIvar設(shè)值的問題。

object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)

object_setIvar 需要傳三個值。 obj是你要賦值的對象。ivar是你通過屬性名拿到的指針。value是你要設(shè)置的值

無論你是百度還是Google,你搜到的runtime相關(guān)教程在這里設(shè)置的都是一個string或者其他對象。但是int,NSInteger,float或者CGFloat它不是一個對象,如果你直接設(shè)置就會報錯。

所以又是一番查找,最后發(fā)現(xiàn)了解決方式。下面是stackoverflow鏈接。
stackoverflow

直接通過指針投射過去! NB

至此,所有難點就攻克。代碼什么的隨便寫寫就完事,重點是思路以及runtime相關(guān)知識。

更重要的是把之前快1000行的垃圾代碼優(yōu)化到了200多行,如果所有的規(guī)范統(tǒng)一的話100行甚至更少

runtime這邊拿到的屬性值。需要記錄一下

BOOL -> TB
int -> Ti
NSNumber -> T@"NSNumber"
NSString -> T@"NSString"
NSInteger -> Tq
NSArray -> T@"NSArray"
id -> T@
float -> Tf
CGFloat -> Td

如果屬性是一個枚舉(NS_ENUM),那么你拿到的是你枚舉對應(yīng)的值。
比如:

typedef NS_ENUM(NSInteger, TestStyle) {

};
拿到的就是Tq。

7-9 Update:

void (*object_setIvarFloat)(id, Ivar, CGFloat) = (void (*)(id, Ivar, CGFloat))object_setIvar;

使用這種方式莫名的會出現(xiàn)賦值無法成功的情況。
最終還是選擇了如果是int這種情況直接采用拿對應(yīng)屬性的地址偏移量直接賦值,下面是代碼:

void setIvarMethod1 (const char * type, id n, Ivar ivar, NSString *obj) {
    ptrdiff_t offset = ivar_getOffset(ivar);
    unsigned char *stuffBytes = (unsigned char *)(__bridge void *)n;
    
    if (strcmp(type, "B") == 0) {
        *((BOOL *)(stuffBytes + offset)) = obj.boolValue;
    }else if (strcmp(type, "q") == 0){
        *((NSInteger *)(stuffBytes + offset)) = obj.integerValue;
    }else if (strcmp(type, "i") == 0) {
        *((int *)(stuffBytes + offset)) = obj.intValue;
    }else if (strcmp(type, "d") == 0 || strcmp(type, "f") == 0){
        *((float *)(stuffBytes + offset)) = obj.floatValue;
    }else{
        object_setIvar(n, ivar, obj);
    }
}

后續(xù)繼續(xù)跟進(jìn)結(jié)果。如果有問題會繼續(xù)更新

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

友情鏈接更多精彩內(nèi)容