技术痛点
后台返回protobuf格式数据对于前端开发实际上并不是很便捷 , 只要增加或者减少哪怕一个字段 , 前端所有关于这个文件的所有业务全部就炸掉了 . 每次都要重新编译probuf文件(请参考我上一篇文章) .
使用protobuf爽的一点是不用建模型 , 因为它本身就是模型 .这对于面向数据模型开发的前端是很友好的 . 凡是都有两面性 , 在不少的业务逻辑中 , 需要修改模型数值 , 这时候protobuf的弊端就展现出来了 , 数值不可更改 . 这是十分痛苦的一件事 . 所以实际上很多情况下 , 都需要自己建模(说来不如Jason) .
所以会写下大量如下的代码
每一次建模, 都需要这样一个一个赋值 , 劳动量极大 , 并且毫无意义 . 搬砖大大降低了工作效率 .
思考
如何动态的去给模型赋值 ? 是的 , runtime . 关于runtime的知识 , 网上很多 , 书籍也比较多 . 这里就不赘述了 .
灵感
在看ibireme大神的YYCategories中关于NSObject的扩展得到灵感 . 关于获取模型属性列表的部分代码也是借(chao)鉴(xi)于此 , 向大神致(gui)敬(tian) .
实现
1 . 创建TestModel , 这是头文件 .
```
#import
@interfaceTestModel :NSObject
/**集团号(测试数据)**/
@property(nonatomic,strong)NSString*clientNo;
/**用户号(测试数据)**/
@property(nonatomic,strong)NSString*userNo;
/**语言号(测试数据)**/
@property(nonatomic,strong)NSString*languageNo;
/**环信用户名(测试数据)**/
@property(nonatomic,strong)NSString*huanXinUserName;
/**环信密码(测试数据)**/
@property(nonatomic,strong)NSString*huanXinPassword;
/**集团全称(测试数据)**/
@property(nonatomic,strong)NSString*clientFullName;
/**集团简称(测试数据)**/
@property(nonatomic,strong)NSString*clientShortName;
/**用户名(测试数据)**/
@property(nonatomic,strong)NSString*userName;
/**密码(测试数据)**/
@property(nonatomic,strong)NSString*password;
/**地址(测试数据)**/
@property(nonatomic,strong)NSString*address;
/**
初始化模型
@paramdata服务器返回的还未解析的protobuf二进制文件(如果是传统Jason,也可以这么干,如果需要我会写一下)
@paramname protobuf名字
@return<#return value description#>
*/
- (instancetype)initWithData:(NSData*)data protobufName:(NSString*)name;
+ (instancetype)modelWithData:(NSData*)data protobufName:(NSString*)name;
/**
测试打印当前Model的属性值
*/
- (void)showModelProperty;
@end
xxx
```
2 . m文件
#import"TestModel.h"
#import
#import
@implementationTestModel
//初始化(对象方法)
- (instancetype)initWithData:(NSData*)data protobufName:(NSString*)name{
if(self= [superinit]) {
[self readPropertyWithProtobuf:data protobufName:name];
}
returnself;
}
//初始化(类方法)
+ (instancetype)modelWithData:(NSData*)data protobufName:(NSString*)name{
return [[self alloc] initWithData:dataprotobuf Name:name];
}
//根据protobuf文件名字和二进制数据,生成protobuf文件并获取属性赋值给Model
- (void)readPropertyWithProtobuf:(NSData*)data protobufName:(NSString*)name{
if(data==nil) {
return;
}
//根据字符串获取类
Class modelClass =NSClassFromString(name);
//利用runtime根据类获取属性列表
NSArray*propertyAry= [self allProperty:modelClass];
//将二进制数据转成model
id model = [[model Classalloc] initWithData:dataerror:nil];
for(inti =0; i < propertyAry.count; i++) {
//获取Get方法
SEL getSEL = [self makeGetterWithPropertyName:propertyAry[i]];
//获取Set方法
SEL setSEL = [self makeSetterWithPropertyName:propertyAry[i]];
if([self respondsToSelector:getSEL]) {
//获取类和方法的签名
NSMethodSignature*signature = [model methodSignatureForSelector:getSEL];
//根据签名获得调用对象
NSInvocation*invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:model];
[invocation setSelector:getSEL];
NSObject*__unsafe_unretainedreturnValue =nil;
[invocationinvoke];
//获取protobuf属性返回值(如同jason的根据key获取value)
[invocation getReturnValue:&returnValue];
if(returnValue !=nil) {
//赋值给Model(关键步骤)
[self performSelectorOnMainThread:setSEL withObject:returnValuewaitUntilDone:[NSThread mainThread]];
}
}
}
}
//获取属性(借鉴于ibireme)
- (NSArray*)allProperty:(Class)class{
unsigned intproperty Count =0;
objc_property_t* properties =class_copyPropertyList(class, &propertyCount);
NSMutableArray* propertyNames = [NSMutableArray array];
for(unsigned int i =0; i < propertyCount; ++i) {
objc_property_tproperty = properties[i];
constchar* name =property_getName(property);
[property NamesaddObject:[NSString stringWithUTF8String:name]];
}
free(properties);
return propertyNames;
}
//根据字符串生成get方法
- (SEL)makeGetterWithPropertyName:(NSString*)propertyName{
return NSSelectorFromString(propertyName);
}
//根据字符串生成set方法
- (SEL)makeSetterWithPropertyName:(NSString*)propertyName{
//首字母大写
NSString*firstWord = [property NamesubstringToIndex:1];
firstWord = [firstWord uppercaseString];
propertyName = [NSString stringWithFormat:@"%@%@",firstWord , [propertyName substringFromIndex:1]];
//拼接上set
propertyName = [NSString stringWithFormat:@"set%@:", propertyName];
return NSSelectorFromString(propertyName);
}
//测试是否赋值成功,可以删掉.
- (void)showModelProperty{
//获取实体类的属性名
NSArray*array = [self allProperty:self.class];
//拼接参数
NSMutableString*resultString = [[NSMutable Stringalloc] init];
for(inti =0; i < array.count; i ++) {
//获取get方法
SEL getSel = [self makeGetterWithPropertyName:array[i]];
if([self respondsToSelector:getSel]) {
//获得类和方法的签名
NSMethodSignature*signature = [self methodSignatureForSelector:getSel];
//从签名获得调用对象
NSInvocation*invocation = [NSInvocation invocationWithMethodSignature:signature];
//设置target
[invocation setTarget:self];
//设置selector
[invocation setSelector:getSel];
//接收返回的值
NSObject*__unsafe_unretainedreturnValue =nil;
//调用
[invocation invoke];
//接收返回值
[invocation getReturnValue:&returnValue];
[resultString appendFormat:@"%@\n", returnValue];
}
}
NSLog(@"%@", resultString);
}
@end
xxxx
调用
//"LoginResMsg"就是protobuf文件的名称字符串userInfo就是二进制数据 .
TestModel*model = [[TestModel alloc] initWithDictionary:userInfo protobufName:@"LoginResMsg"];
//测试是否赋值成功
[model showModelProperty];
打印
完结
因为Jason的解析已经有很多成熟的框架 , YYModel , MJExtension 等等 .protobuf这东西用得不多 , 请参考 .