> 软件编程 > IOS开发 >
实现一个JSON映射库 2017-04-10 12:42 出处:清屏网 人气:
JSON序列化框架是iOS开发的必备代码库,我这里指的序列化是指JSON<->Models之间的转换。目前比较流行的有Mantle,JSONModel,还有国产的MJExtension,YYModel等。这些库都很优秀可以满足日常大部分的需求。 为什么要重复造轮子
我在项目中用过Mantle和JSONModel,用起来感觉都挺好的,但是它们都有一些方面我感觉不是很喜欢。
在Mantle和JSONModel之间选一个话,我会选择Mantle。Mantle非常稳定,功能强大,而且设计更加合理,功能强大。但是源代码而言我比较喜欢JSONModel的风格,思路清晰,风格统一。Mantle不知道是设计比较复杂还是怎样,代码看起来没有那么整洁清晰,当然这只是个人的一些看法。虽然说一些测评文章指出Mantle的性能在几个流行的框架中算是垫底的,但实际来看,稳定性和好的设计才是我们开发重点关注的点。
实际上Mantle已经够用了,对于我自己来说它可能太重了。我的理解中JSON序列化的框架的职责是将JSON数据转化成Model。这其实是一个很自然的过程,我自己的需求只是需要最简单的方法转化成NSObject就行了,我不需要在框架内进行类似于NSDate这种复杂的转换。所以我打算实现一个最简易映射框架,同时也作为一个学习过程。
基本思路大部分JSON映射框架都是基于Cocoa强大的runtime能力实现的。我们可以动态的获取类的properties,然后使用强大KVC来实现动态赋值。
我的思路非常简单直接:
1.获取类所有的Properties,然后解析出每个property的必要信息(类型,keypath等等)。
2.使用JSONSerialization将Data或者String转换成NSDictionary或者NSArray,枚举类的properties通过key来获取NSDictionary里面的value然后赋值到对象里面。
获取Class的properties利用oc的runtime方法,我们可以用一个循环拿到这个类的所有properties。为此我增加了一个NSObject的category:
+ (NSArray<JDCClassProperty *> *)jdc_classProperties { //这里我们缓存一下properties一定程度上提升效率。 NSArray *properties = [self getCacheProperties]; if (!properties) { Class cls = self; NSMutableArray *all = [NSMutableArray new]; //我们需要遍历整一个继承链来获取所有的properties while(cls != [NSObject class]){ [all addObjectsFromArray:[self jdc_getProperties:cls]]; cls = [cls superclass]; } properties = all; [self setCachedProperties:all]; } return properties; } + (NSArray *)jdc_getProperties:(Class)class { unsigned int pCount = 0; //使用runtime相关方法来获取properties。 objc_property_t *properties = class_copyPropertyList(class, &pCount); NSMutableArray *pArray = [NSMutableArray new]; for(int i = 0 ; i < pCount ; i++){ objc_property_t property = properties[i]; //我在JDCClassProperty初始化阶段对property 进行解析 JDCClassProperty *clsProperty = [[JDCClassProperty alloc] initWithProperty:property]; if (!clsProperty.isReadyOnly) { [pArray addObject:clsProperty]; } } return pArray; } + (NSArray *)getCacheProperties { return objc_getAssociatedObject(self, &kAssociatedCachePropertiesKey); } + (void)setCachedProperties:(NSArray *)properties { objc_setAssociatedObject(self, &kAssociatedCachePropertiesKey,properties, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } 解析Property拿到的原始property是一种Type Encoding的字符串形。Apple有文档解释Type Encoding 。我们需要对其进行解析,一个典型的NSString Encoding为这种形式:
T@"NSString",&,N,V_login这个字符串包含了property的类型,读写属性,以及key(name)。把这些信息解析出来即可。我们看一下形式大概就知道Encoding的格式,具体可以去阅读以下文档。解析代码:
- (void)_inspectProperty:(objc_property_t)property { NSString *str = [self getPropertyAttributeString:property]; NSArray *components = [str componentsSeparatedByString:@","]; NSString *token = components[0]; //R代表只读属性,这里我们忽略只读属性 _isReadyOnly = [components containsObject:@"R"]; //'@'代表的是对象,是我们重点关注的类型。 if ([token characterAtIndex:1] == '@' && token.length > 3 && [token characterAtIndex:2] == '\"') { NSString *name = [token substringWithRange:NSMakeRange(3, token.length-4)]; _propertyTypeName = name; //JSONSerialization所支持的标准JSon类型。 if (sAllowedJSONTypes[name]) { _propertyType = JDCClassPropertyStandandJsonType; _isArray = [name isEqual:@"NSArray"]; }else{ //自定义类型,我们需要重点处理的类型。 _propertyType = JDCClassPropertyCustomType; } }else{ NSString *encodeStr = [token substringWithRange:NSMakeRange(1, 1)]; NSNumber *type = sTypeMappings[encodeStr]; if (type) { _propertyType = [type unsignedIntegerValue]; }else{ _propertyType = JDCClassPropertyOtherType; } } }