上一篇中《從YYModel源碼中可以學(xué)到什么:后篇》中主要學(xué)習(xí)了YYModel
的源碼結(jié)構(gòu),只是分享了YYModel
整體結(jié)構(gòu)。
創(chuàng)新互聯(lián)公司于2013年創(chuàng)立,是專業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項目成都做網(wǎng)站、網(wǎng)站設(shè)計、外貿(mào)營銷網(wǎng)站建設(shè)網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元貴德做網(wǎng)站,已為上家服務(wù),為貴德各地企業(yè)和個人服務(wù),聯(lián)系電話:18982081108
承接上篇,本文將解讀YYModel
如何進(jìn)行JSON模型轉(zhuǎn)換的,接下來一起揭開YYModel
的神秘面紗吧!
首先來看JSON是如何轉(zhuǎn)為Model。查看YYModel
的接口,提供了一個方法:
+ (instancetype)yy_modelWithJSON:(id)json;
注意json
為id
類型,接收三種不同類型參數(shù)NSString
,NSData
,NSDictionay
。下面是內(nèi)部實現(xiàn):
+ (instancetype)yy_modelWithJSON:(id)json {
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
return [self yy_modelWithDictionary:dic];
}
方法中調(diào)用了一個私有方法_yy_dictionaryWithJSON:
,該方法將id
類型的json
(NSDictionary, NSString, NSData)轉(zhuǎn)為字典。
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
// 驗證json
if (!json || json == (id)kCFNull) return nil;
// 兩個臨時變量
NSDictionary *dic = nil;
NSData *jsonData = nil;
// 根據(jù)json類型,進(jìn)行相應(yīng)處理
if ([json isKindOfClass:[NSDictionary class]]) {
dic = json;
} else if ([json isKindOfClass:[NSString class]]) {
jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
}
// 使用NSJSONSerialization將Data轉(zhuǎn)為字典
if (jsonData) {
dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
}
return dic;
}
該方法很簡單,就是判斷
id
類型是指(NSDictionary,NSString,NSData)中的哪一個,分別處理。在
json == (id)kCFNull
中kCFNull
是什么意思?這條語句起到什么作用?
nil
: 指向OC中對象的空指針
Nil
: 指向OC中類的空指針
NULL
:定義其他類型(基本類型、C類型)的空指針
NSNull
:集合對象中,表示空值的對象。如給數(shù)組設(shè)置空值,使用NSNull
,而不能使用nil
。
kCFNull
是NSNull
的單例。
if (!json || json == (id)kCFNull) return nil;
該判斷的意思是,json對象不存在,或者為空是返回nil
。
獲取到字典后,調(diào)用字典轉(zhuǎn)Model方法:
+ (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary
此方法也是在.h
文件中暴露出來的方法。接下來查看具體實現(xiàn):
/**
字典轉(zhuǎn)model
@param dictionary 字典
@return 返回Model對象
*/
+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
// 1. 驗證,空值,nil和是否是字典
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
Class cls = [self class];
// 2. 創(chuàng)建一個YYModelMeta對象
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
// 判斷是否需要自定義返回模型的類型,這是YYModel協(xié)議中的內(nèi)容,算是附加功能暫時先忽略,后面在介紹。
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
// 3. 創(chuàng)建model對象
NSObject *one = [cls new];
// 4. 關(guān)鍵方法
if ([one yy_modelSetWithDictionary:dictionary])
return one;
return nil;
}
這個方法主要的任務(wù)是調(diào)用了yy_modelSetWithDictionary:
方法。這個方法也是在.h
文件中暴露的,它的作用是根據(jù)字典初始化模型。代碼實現(xiàn):
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
// 1. 值和類型驗證
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
// 2. 創(chuàng)建Modelmeta對象
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
// 屬性個數(shù),如為零返回。創(chuàng)建失敗
if (modelMeta->_keyMappedCount == 0) return NO;
// YYModel協(xié)議,暫且忽略
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
// 3. 創(chuàng)建結(jié)構(gòu)體
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
// 模型元值數(shù)量和字典數(shù)量關(guān)系
// 1. 通常情況是兩者相等,
// 2. 模型元鍵值少于字典個數(shù)
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
// 調(diào)用ModelSetWithDictionaryFunction方法,這是核心方法。
// 參數(shù):
// 1. 要操作的字典
// 2. 為每個鍵值對調(diào)用一次的回調(diào)函數(shù)
// 3. 指針大小的程序定義的值,作為第三個參數(shù)傳遞給應(yīng)用程序函數(shù),但此函數(shù)未使用該值. 與參數(shù)2適應(yīng)
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
// 是否存在映射keyPath屬性元
if (modelMeta->_keyPathPropertyMetas) {
// 每個keypath都執(zhí)行ModelSetWithPropertyMetaArrayFunction
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
// 是否存在映射多個key的屬性元
if (modelMeta->_multiKeysPropertyMetas) {
// 每個keypath都執(zhí)行ModelSetWithPropertyMetaArrayFunction
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
} else {
// 每個keypath都執(zhí)行ModelSetWithPropertyMetaArrayFunction
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
// 忽略
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
上面注釋該方法大致干了幾件事。
YYModelMeta
對象ModelSetContext
結(jié)構(gòu)體ModelSetWithDictionaryFunction
方法關(guān)于ModelSetContext
是一個結(jié)構(gòu)體。包含模型元,模型實例和待處理字典。
typedef struct {
void *modelMeta; ///< _YYModelMeta
void *model; ///< id (self)
void *dictionary; ///< NSDictionary (json)
} ModelSetContext;
通過上面的分析,線路越來越清晰,下面看一下核心方法ModelSetWithDictionaryFunction
將字典的鍵值對取出賦值給Model。
// 獲取到字典的鍵值對,和上下文信息。
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
// 上下文,包含模型元,模型實例,字典
ModelSetContext *context = _context;
//模型元
__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
//在模型元屬性字典中查找鍵值為key的屬性
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
// 模型實例
__unsafe_unretained id model = (__bridge id)(context->model);
// 核心內(nèi)容,遍歷所有的屬性元。知道_next = nil
while (propertyMeta) {
if (propertyMeta->_setter) {
// 最終轉(zhuǎn)換(高潮)
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
}
propertyMeta = propertyMeta->_next;
};
}
該方法主要任務(wù)是,遍歷所有的屬性元,調(diào)用模型屬性賦值方法ModelSetValueForProperty
。
最終實現(xiàn)。也就是YYModel
的最最最核心的部分。不過,這個方法長的離譜。由于涉及到較多的編碼類型,需要對不同的類型區(qū)分處理,導(dǎo)致代碼過長。
由于ModelSetValueForPorperty
代碼較長,這里不再復(fù)制代碼。只梳理一下實現(xiàn)的邏輯,注釋代碼在Github自行查閱。
isCNumber
, nsType
和其他類型來區(qū)分處理。isCNumber
此時調(diào)用方法ModelSetNumberToPorperty
。該方法將NSNumber類型的值根據(jù)不同的編碼賦值給屬性。nsType
是YYEncodingNSType
枚舉類型,枚舉Foundation
所有的類型,對不同的類型進(jìn)行處理。YYEncodingType
枚舉定義的情況。調(diào)用ModelSetNumberToPorperty
方法,該方法作用是:識別null
、bool
、123.23
、"123.45"
等類型,轉(zhuǎn)為NSNumber。
以上就是JSON轉(zhuǎn)Model全部過程。
相對JSON
轉(zhuǎn)Model
來說,Model
轉(zhuǎn)JSON
就簡單多了。可以使用NSJSONSerialization
類將Foundation對象轉(zhuǎn)為JSON。但是轉(zhuǎn)為JSON必須滿足一下條件。
NSArray
或NSDictionary
NSString
,NSNumber
,NSArray
,NSDictionary
或者NSNull
NSString
NAN
或無窮大官方說明NSJSONSerialization文檔
接下來看看是如何轉(zhuǎn)換的,首先看到的方法是:
- (id)yy_modelToJSONObject;
該方法的時下很簡單只有幾行代碼。
- (id)yy_modelToJSONObject {
// 關(guān)鍵方法
id jsonObject = ModelToJSONObjectRecursive(self);
if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;
if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;
return nil;
}
內(nèi)部調(diào)用了方法ModelToJSONObjectRecursive
方法。作者對該方法注釋中說,返回一個有效的JSON
對象(NSArray/NSDictionary/NSString/NSumber/NSNull)
或者為空。查看源碼,該方法做了幾件事
承接上文,本文對JSON轉(zhuǎn)Model主線路進(jìn)行詳細(xì)的分析,關(guān)于一些輔助功能沒有分析。還有,一些代碼比較長,由于篇幅限制,所以注釋代碼放在《GitHub》。
由于筆者能力有限,不可避免的出現(xiàn)錯誤,歡迎大家指正。
個人博客Owenli
微博Owenli_千
新聞名稱:從YYModel源碼中可以學(xué)到什么:后篇
文章位置:http://chinadenli.net/article2/ihocic.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供服務(wù)器托管、網(wǎng)站改版、商城網(wǎng)站、網(wǎng)站建設(shè)、、動態(tài)網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)