現(xiàn)狀
業(yè)務(wù)這邊之前有需求各種config脂倦,從后端拿到數(shù)據(jù)番宁,一個(gè)字典挨個(gè)解析。
最早的寫(xiě)法如下:
...
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;
}
}
** 很多類似的寫(xiě)法
...
代碼臃腫不說(shuō)狼讨,很丑贝淤。
但是柒竞,因?yàn)樯瞎虐姹镜脑蛘愔卸x的數(shù)據(jù)類型也是隨心所欲。
int NSInteger NSString NSNumber BOOL float CGFloat
幾乎包含了所有的能用到的類型...
再加上這邊要給QA做一個(gè)開(kāi)關(guān)的需求朽基,本地隨便修改這些config布隔。所有有了這篇記錄。
當(dāng)然如果你本身代碼很規(guī)范稼虎,直接用Mantle或者其他的工具解析就完事了衅檀。我這邊因?yàn)槭巧瞎糯a的關(guān)系,不可能直接修改改動(dòng)太大霎俩,所以才出此下策哀军。
大概思路
- 后端或者本地緩存拿到數(shù)據(jù)。
- 如果是QA環(huán)境需要檢查本地的debug數(shù)據(jù)打却,看是否設(shè)置過(guò)本地的config杉适,如果設(shè)置過(guò)則需要將debug數(shù)據(jù)更新上去。
- 通過(guò)便利拿到的數(shù)據(jù)通過(guò)key值通過(guò)runtime的object_setIvar一一對(duì)應(yīng)的設(shè)給對(duì)應(yīng)的Model柳击。
難點(diǎn)一
- 代碼里面的屬性名稱跟后端的Key對(duì)不上猿推。
比如,后端的key是TestKey,但是代碼里面的屬性名稱是1111蹬叭。(只是比喻) - 有一些config在設(shè)計(jì)的時(shí)候不是按照一個(gè)規(guī)范來(lái)的藕咏。需要對(duì)這個(gè)config做一個(gè)特殊的處理。
難點(diǎn)解決
- 照著Mantle的模式寫(xiě)了一個(gè)方法返回一個(gè)字典秽五。對(duì)應(yīng)的Key孽查、Value分別是后端的Key以及本地的屬性名稱。
- 另外一個(gè)方法坦喘,返回的是需要特殊處理的key卦碾。
這樣寫(xiě)的好處是便于后續(xù)擴(kuò)展。
如果真的需要兩端不相同起宽,只需在1方法中加上對(duì)應(yīng)的Key洲胖、Value就行。如果需要特殊處理只需在2加對(duì)應(yīng)的值就行坯沪。并且绿映,后續(xù)添加config只需.h 方法中新增就行,無(wú)需多加代碼腐晾,提高效率叉弦。
難點(diǎn)二
在寫(xiě)代碼過(guò)程中,發(fā)現(xiàn)runtime的方法object_setIvar設(shè)值的問(wèn)題藻糖。
object_setIvar(id _Nullable obj, Ivar _Nonnull ivar, id _Nullable value)
object_setIvar 需要傳三個(gè)值淹冰。 obj是你要賦值的對(duì)象。ivar是你通過(guò)屬性名拿到的指針巨柒。value是你要設(shè)置的值
無(wú)論你是百度還是Google樱拴,你搜到的runtime相關(guān)教程在這里設(shè)置的都是一個(gè)string或者其他對(duì)象。但是int洋满,NSInteger晶乔,float或者CGFloat它不是一個(gè)對(duì)象,如果你直接設(shè)置就會(huì)報(bào)錯(cuò)牺勾。
所以又是一番查找正罢,最后發(fā)現(xiàn)了解決方式。下面是stackoverflow鏈接驻民。
stackoverflow
直接通過(guò)指針投射過(guò)去翻具! NB
至此,所有難點(diǎn)就攻克回还。代碼什么的隨便寫(xiě)寫(xiě)就完事裆泳,重點(diǎn)是思路以及runtime相關(guān)知識(shí)。
更重要的是把之前快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
如果屬性是一個(gè)枚舉(NS_ENUM),那么你拿到的是你枚舉對(duì)應(yīng)的值。
比如:
typedef NS_ENUM(NSInteger, TestStyle) {
};
拿到的就是Tq帜篇。
7-9 Update:
void (*object_setIvarFloat)(id, Ivar, CGFloat) = (void (*)(id, Ivar, CGFloat))object_setIvar;
使用這種方式莫名的會(huì)出現(xiàn)賦值無(wú)法成功的情況糙捺。
最終還是選擇了如果是int這種情況直接采用拿對(duì)應(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é)果笙隙。如果有問(wèn)題會(huì)繼續(xù)更新