Github地址html
看到JSON模型转换库,相信你们都没兴趣了,由于各自项目里确定早都有了。其实也不是我想重复造轮子,主要是这个库其实早在2012年这个库就有了,而那时既没有小码哥的MJExtension,也没有牛逼闪闪的YYModel。那时的JSON模型转换库没有几个,并且都又大又难用,因此我当时本身写了一个。这个库在公司的项目中一直沿用至今,中间我陆续作了点扩展,也优化了一下性能,总体仍是很是稳定可用的。git
今天忽然想把它拿出来,给你们分享一下。由于这个库虽然不是性能最强最全面的,可是它足够的简单,直白,只有两个文件,300行代码,可供初学者学习,也可用你们自行扩展知足本身的需求。github
和其余库基本上是差很少的。例子能够参考main.m。json
这个库自己是用OC写的,若是是纯Swift项目就不建议用了。可是对于OC和Swift混编项目,尤为是从OC转过来的项目,仍是很好用的。由于OC下的转换一般只要一行代码,项目切换到Swift之后,若是用SwiftyJSON仍是挺不习惯的。用这个库可让OC和Swift里的开发习惯保持基本一致。swift
Swift下的示例能够参考SwiftJsonTest.swift。 具体有以下几点要求:数组
NSObject
,并用@objcMembers
声明。Int
,并用@objc
声明。代码示例以下:性能优化
@objc enum OrderState: Int {
case created = 1
case completed = 2
case canceled = 3
}
@objcMembers class OrderInfo: NSObject, JSONEx {
var state: OrderState = .created
var ID: String?
var count: Int = 0
var product: ProductInfo?
static func customPropertyNameForKeys() -> [String : String] {
return ["ID": "id"]
}
}
@objcMembers class School: NSObject, JSONEx {
var address: String?
var students: [Student] = []
static func arrayPropertyItemClasses() -> [String : String] {
return ["students": "JSONEx.Student"]
}
}
复制代码
调用的时候一句就能够了:bash
let obj: PhoneInfo = PhoneInfo(dictionary: dic)
复制代码
和OC下的使用是基本一致的。多线程
总体的实现都很是简单直白,只有字典NSDictionary给对象属性赋值的部分代码稍微多一点,这个赋值是基于KVC作的,若是属性有setter方法的话KVC的效率是不差的。对象的基类属性的赋值是经过递归作的。 主要的方法以下:app
@implementation NSDictionary (NSDictionary_ObjectMapping)
- (void)setObject:(id)obj fromClass:(Class)cls
{
unsigned int count = 0;
objc_property_t *properties = class_copyPropertyList(cls, &count); //获取该类的全部属性
for (int i = 0; i < count; i++) {
objc_property_t property = properties[i];
const char* key = property_getName(property);
NSString* strKey = [NSString stringWithCString:key encoding:NSUTF8StringEncoding];
id value = [self objectForKey:strKey];//从字典里按属性名获取value
if (value == nil) {
//对象属性名和JSON关键词key不一致的状况,获取属性名对应的key
NSString *jsonKey = [self getObjCustomPropertyKey:obj properyName:strKey];
if (jsonKey != nil) {
value = [self objectForKey:jsonKey];//再次从字典里获取value
}
}
Class propertyCls = [self getPropertyClass:property];//获取属性的类
if (propertyCls == nil || [self isFoundationClass:propertyCls]) {
if(value != nil && value != (id)kCFNull) {
if (propertyCls == [NSURL class] && [value isKindOfClass:[NSString class]]) {
[obj setValue:[NSURL URLWithString:value] forKey:strKey];
} else if (propertyCls == [NSArray class] && [value isKindOfClass:[NSArray class]]) {
//数组属性里的元素是自定义类型
Class cls = [self getObjArrayPropertyClass:obj properyName:strKey];
if (cls) {
[obj setValue:[cls objectArrayWithArray:value] forKey:strKey];
} else {
[obj setValue:value forKey:strKey];
}
} else {
[obj setValue:value forKey:strKey];//经过KVC给对象的属性赋值
}
}
} else {//自定义类型属性
if(value != nil && [value isKindOfClass:[NSDictionary class]]) {
id subObj = [[propertyCls alloc] init];
[value setObject:subObj fromAllClass:propertyCls];
[obj setValue:subObj forKey:strKey];
}
}
}
free(properties);
}
@end
复制代码
步骤以下:
class_copyPropertyList
获取类的属性列表Class
主要优化了两个地方,一是获取属性类型的地方,而是访问扩展设置方法的地方。
- (Class)getPropertyClass:(objc_property_t)property
{
unsigned int attrCount;
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = 0; i < attrCount; i++) {
const char *name = attrs[i].name;
const char *value = attrs[i].value;
if (name[0] == 'T') {
if (strlen(value) > 2 && value[0] == '@' && value[1] == '\"') {
char *p = strrchr(value + 2, '\"');
if (p) {
*p = '\0';
free(attrs);
return objc_getClass(value + 2);
}
}
break;
}
}
free(attrs);
return nil;
}
复制代码
这里直接对char字符串操做,效率高不少,以前是先转成NSString对象的。关于属性的信息你们能够查这里Property Attribute Description Examples。 对象是T@
开头,后面接着的就是类型。
还有一个优化是关于扩展设置方法的:
@protocol JSONEx <NSObject>
@optional
+ (NSDictionary<NSString *, NSString *> *)arrayPropertyItemClasses;//数组属性里元素的类型
+ (NSDictionary<NSString *, NSString *> *)customPropertyNameForKeys;//属性名和JSON关键词的映射
@end
复制代码
每次都读取这两个方法很耗时。我参考YYModel的作法把这两个方法返回的设置一次性读到一个全局的静态字典里,再来访问这个静态变量,速度就快了。代码以下:
// 用静态字典存取类的customPropertyNameForKeys方法的值,以免函数调用,提升访问速度
static NSDictionary* customPropertyKeyForClass(Class cls) {
if (!cls || ![cls respondsToSelector:@selector(customPropertyNameForKeys)]) return nil;
static CFMutableDictionaryRef cache;
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
NSDictionary *dic = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
dispatch_semaphore_signal(lock);
if (!dic) {
dic = [(id<JSONEx>)cls customPropertyNameForKeys];
if (dic) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(dic));
dispatch_semaphore_signal(lock);
}
}
return dic;
}
复制代码
这里用GCD的信号量保证多线程状况下的静态字典的读写互斥。
最终的结果: 用YYModel里的用例,循环解析10000遍,在iPhone 7模拟器上,耗时大幅优于MJExtention。
JSONEx: 640.83ms YYModel: 176.11ms MJExtension: 2196.75ms