当前位置: 首页 > 科技观察

iOS手动构建一个JSONModel转换库

时间:2023-03-14 16:47:13 科技观察

观察下面的JSON数据和Model数据NSString*girlFriend=@"cabbage";idparmenters=@{@"girlFriend":girlFriend,@"age":@22.1,@"name":@"Lastdays",@"time":@"2016-03-185:55:49+0000"};@interfaceModel:NSObject@propertyNSNumber*age;@propertyNSString*名称;@propertyNSString*girlFriend;@propertyNSData*时间;@end一开始仔细想过,如何动态添加属性值,并根据对应的属性赋值,并保证类型正确。这是我首先考虑的问题。但核心问题是动态实现。让我们逐步解决问题。首先我们先获取Model属性,通过获取Model的一些信息来获取Model属性。运行时提供class_copyPropertyList来获取属性列表。OK,我们来看看用它得到的数据长什么样子。查看运行源代码/**************************************************************************class_copyPropertyList。**********************************************************************/objc_property_t*class_copyPropertyList(Classcls,unsignedint*outCount){old_property_list*plist;uintptr_tierator=0;old_property**result=nil;无符号数=0;无符号整数,我;如果(!cls){如果(outCount)*outCount=0;返回零;}mutex_locker_tlock(classLock);迭代器=0;while((plist=nextPropertyList(cls,&iterator))){count+=plist->count;}if(count>0){result=(old_property**)malloc((count+1)*sizeof(old_property*));p=0;迭代器=0;while((plist=nextPropertyList(cls,&iterator))){for(i=0;icount;i++){结果[p++]=property_list_nth(plist,i);}}结果[p]=无;}如果(输出计数)*输出计数=计数;返回(objc_property_t*)结果;}typedefstructold_property*objc_property_t;structold_property{constchar*name;常量*属性;};从以上三段runtime源码,从课本上可以判断,返回的结果其实是一些old_property,每个old_property包含了对应的name等信息,可以总结为**class_copyPropertyList**来获取Model属性列表,属性列表中的objc_property_t包含属性的类型、名称等信息。根据刚才的分析,设计了如下结构:bash-(id)modelToJsonObject:(NSObject*)model{Classcls=self.class;unsignedintcount属性=0;objc_property_t*propertys=class_copyPropertyList(cls,&countProperty);NSMutableDictionary*dic=[NSMutableDictionarynew];for(unsignedinti=0;iname;}下一步是通过property_getName获取属性类型和一些其他信息。获取属性信息其实和上面的原理类似。我们使用property_copyAttributeList,objc_property_attribute_t*property_copyAttributeList(objc_property_tprop,unsignedint*outCount){if(!prop){if(outCount)*outCount=0;返回零;}mutex_locker_tlock(classLock);返回(旧属性(prop)->属性,outCount);}看到这里,不继续分析源码了,其实可以看出属性就是我们想要的信息,其实每个属性也有自己对应的属性。属性是什么样的?查看源码,找到了答案typedefstruct{constchar*name;常量*值;}objc_property_attribute_t;添加断点,看到name为T,value为NSNumber。让我们获取NSNumber的属性类型。for(unsignedinti=0;i3){字符名[len-2];名字[len-3]='';memcpy(名称,attrs[i].value+2,len-3);_typeClass=objc_getClass(名字);}}}基本上我们要的信息基本都已经获取了,现在下一步就是进行动态设置了。中间打个插曲,简单说一下Objc是动态语言。在[receivermessage]执行过程中,[receivermessage]会被动态编译。Objc是动态语言,所以会尽量把编译和连接等到运行时再做。runtime的实时运行系统就是执行编译后的代码。在这个消息发送过程中,objc_msgSend起到了非常重要的作用,所以我们可以主动触发objc_msgSend来模拟getter和setter方法来获取属性值,或者创建。我们通过SEL来定义选择器,什么是选择器呢?它是方法名称的唯一标识符。按照刚才的思路,写出来的代码***是这样的——(instancetype)initWithProperty:(objc_property_t)property{_property=property;constchar*name=property_getName(属性);如果(名称){_propertyName=[NSStringstringWithUTF8String:name];}unsignedintattrCount;objc_property_attribute_t*attrs=property_copyAttributeList(property,&attrCount);for(unsignedinti=0;i3){字符名[len-2];名字[len-3]='';memcpy(名称,attrs[i].value+2,len-3);_typeClass=objc_getClass(名字);}}}NSString*setter=[NSStringstringWithFormat:@"set%@%@:",[_propertyNamesubstringToIndex:1].uppercaseString,[_propertyNamesubstringFromIndex:1]];_setter=NSSelectorFromString(setter);_getter=NSSelectorFromString(_propertyName);返回自我;}基本准备和一些问题都解决了,接下来就可以写函数了。JSON转Model根据刚才所说,我们可以主动触发objc_msgSend模拟setter方法创建属性值。设计以下方法-(void)LYModelSetPropertyWithModel:(id)modelvalue:(id)valuepropertyInfo:(PropertyInfo*)propertyInfo{((void(*)(id,SEL,id))(void*)objc_msgSend)((id)模型、propertyInfo.setter、值);}我们把Model的所有属性都存储在NSMutableDictionary中,key是属性名,value是PropertyInfo。现在你可以动态设置它了idkey,值;NSArray*keys=[dictonaryallKeys];NSUIntegercount=[键数];for(inti=0;i