現(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)系,不可能直接修改改動太大,所以才出此下策。
大概思路
- 后端或者本地緩存拿到數(shù)據(jù)。
- 如果是QA環(huán)境需要檢查本地的debug數(shù)據(jù),看是否設(shè)置過本地的config,如果設(shè)置過則需要將debug數(shù)據(jù)更新上去。
- 通過便利拿到的數(shù)據(jù)通過key值通過runtime的object_setIvar一一對應(yīng)的設(shè)給對應(yīng)的Model。
難點一
- 代碼里面的屬性名稱跟后端的Key對不上。
比如,后端的key是TestKey,但是代碼里面的屬性名稱是1111。(只是比喻) - 有一些config在設(shè)計的時候不是按照一個規(guī)范來的。需要對這個config做一個特殊的處理。
難點解決
- 照著Mantle的模式寫了一個方法返回一個字典。對應(yīng)的Key、Value分別是后端的Key以及本地的屬性名稱。
- 另外一個方法,返回的是需要特殊處理的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ù)更新