前言
前些天,程序出现了一个bug:数据莫名的丢失。由于业务复杂,折腾了好久才解决,在网上也没有找的答案, 故,记下来。
现象
- 将 protobuf 的 Data 转换为 Json
- Json 转为 Model
- 发现 Model 的有些字段数据丢失
- 发现是有 Has* 对应的 字段变为nil了
- 这个问题发生在32位真机上。
知识点
- protobuf 提供了 Data 与 Model 互转的方法
- protobuf 没有提供 Json 和 Model 互转的方法(ios 上我没有找到,有找到的麻烦通知一声)
- 32位机和64位机在遍历Dictionary时可的顺序不一样。(具体还没有深入研究)
- protobuf 在转Model时 会为 Message 嵌套 属性 添加 has* 字段,为数组添加_count字段 (阅读源码发现有这个
SEL hasOrCountSel_
私有变量控制,且这两个新增加的属性的赋值会影响到对应的字段)
例如
Message *msg = (一个有数据的msg)
content 是嵌套的Message
NSLog(@"msg.content = ",msg.content)
//有数据
msg.hasContent = YES;
NSLog(@"msg.content = ",msg.content)
//打印msg.content =nil
修改方案
本着不修改第三方库的原则,项目中使用了 YYModel。添加了个分类 GPBMessage (YYModel)
在 Json 转Model 时过滤掉has,_count 先这样解决了问题。
- (NSString *)uppercaseFirstCharacter:(NSString*)str {
NSRange range = NSMakeRange(0,1);
NSString *upperFirstCharacter = [[str substringToIndex:1] uppercaseString];
return [str stringByReplacingCharactersInRange:range withString:upperFirstCharacter];
}
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic {
NSMutableDictionary *filteredDic = [[NSMutableDictionary alloc] initWithDictionary:dic];
GPBDescriptor *descriptor = [self descriptor];
for (GPBFieldDescriptor *fd in descriptor.fields) {
Ivar ivar = class_getInstanceVariable([fd class], "hasOrCountSel_");
// 当使用ARC后请使用 BOOL ok = NO; ok = object_getIvar(fd, ivar);
// https://stackoverflow.com/questions/8356232/object-getivar-fails-to-read-the-value-of-bool-ivar
unsigned char val;
val = ((unsigned char (*)(id, Ivar))object_getIvar)(fd, ivar);
// RCTrace(@"Looks like I got: %u (or character %c) %@", val, val,fd.name);
if (val != 0 ) {
NSString *countKey = [NSString stringWithFormat:@"%@_Count",fd.name];
[filteredDic removeObjectForKey:countKey];
NSString *hasKey = [NSString stringWithFormat:@"has%@",[self uppercaseFirstCharacter:fd.name]];
[filteredDic removeObjectForKey:hasKey];
}
}
return filteredDic;
}
网友评论