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