目錄
- 1 JsonModel的簡介以及XML簡介
- 2 JsonModel的入?yún)⑴c出參( NSString NSData Dictionary 與json數(shù)據(jù)相互轉(zhuǎn)換)
- 3 jsonModel原理 (如果想詳細(xì)了解原理請先看下面兩個文章)
KVC與KVO
OC的反射機(jī)制
依照習(xí)慣我們還是先介紹下jsonModel到底是個什么東東呢?
- 答:IOS設(shè)備時常需要和服務(wù)器----一般來說是遠(yuǎn)程服務(wù)器進(jìn)行數(shù)據(jù)交換。輕量級的APP應(yīng)用只需少量的數(shù)據(jù)交互,例如每隔幾分鐘獲取一次是否有更新的消息。另一些APP應(yīng)用則需要與后臺服務(wù)器有類似分享信息,閱讀關(guān)注,上傳一張照片等操作這里就存在了交互的問題,交互分為兩種
(這里下面會對兩種交互進(jìn)行講解json轉(zhuǎn)換model以及string/字典/NSData轉(zhuǎn)換json進(jìn)行詳細(xì)解答)
用XML來發(fā)送這些信息給Web服務(wù)器已經(jīng)過時了。越來越多的移動應(yīng)用更傾向于用JSON這種數(shù)據(jù)格式。一旦計(jì)劃開發(fā)移動應(yīng)用的并有與后臺通信的需求,則要用JSON數(shù)據(jù)格式與服務(wù)器通信互相通訊。
JsonModel是用Objective-C寫的開源庫。它包含了接受發(fā)送、解析JSON數(shù)據(jù),用JSON數(shù)據(jù)驅(qū)動初始化你的類,還能夠檢驗(yàn)JSON和嵌套模型等功能。
- (為什么說xml過時了呢?首先我們看下xml格式的代碼) xml數(shù)據(jù)展示
<?xml version="1.0" encoding="UTF-8"?>
<recipe>
<recipename>Ice Cream Sundae</recipename>
<ingredlist>
<listitem>
<quantity>3</quantity>
<itemdescription>chocolate syrup or chocolate fudge</itemdescription>
</listitem>
<listitem>
<quantity>1</quantity>
<itemdescription>nuts</itemdescription>
</listitem>
<listitem>
<quantity>1</quantity>
<itemdescription>cherry</itemdescription>
</listitem>
</ingredlist>
<preptime>5 minutes</preptime>
</recipe>
- json數(shù)據(jù)展示
{
"number":"啦啦啦啦啦",
"name":"啦啦啦啦啦",
"age": 1000
}
1.是不是看著就比json代碼亂。
-
2.其次xml代碼這邊需要用SAX和DOM或者TBXML, TouchXML, KissXML, TinyXML框架進(jìn)行解析,然后將解析之后的數(shù)據(jù)進(jìn)行拼接,拼接之后最后兩段的處理還需要還原成json數(shù)據(jù)進(jìn)行字典處理,所以現(xiàn)在xml數(shù)據(jù)慢慢已經(jīng)被淡化了。
那么下面我們言歸正傳還是對jsonmodel進(jìn)行分析
上面說到我們常見的兩種形式就是將json數(shù)據(jù)轉(zhuǎn)換成模型model的數(shù)據(jù)或者將string數(shù)據(jù)轉(zhuǎn)換為json的數(shù)據(jù),以及字典轉(zhuǎn)json數(shù)據(jù)再或者Data轉(zhuǎn)json,那么其實(shí)這兩種情況就是入?yún)⒑头磪⒌膬煞N形式(json轉(zhuǎn)換為其他形式叫做出參,其他形式轉(zhuǎn)換為json叫做入?yún)ⅲ?
-
入?yún)g:也就是指我們常說的給后臺人員數(shù)據(jù),我們需要把一些數(shù)據(jù)進(jìn)行拼接轉(zhuǎn)換成json串的形式傳遞給后臺人員然后他自己在進(jìn)行解析,如果你還是沒搞懂那么看下圖--
入?yún)tring轉(zhuǎn)json 好看完圖我們?nèi)タ纯磈sonModel源碼里面寫的string轉(zhuǎn)換json的方法以及字典轉(zhuǎn)json或者是NSData轉(zhuǎn)json的實(shí)現(xiàn)--如圖--

-
之后我又點(diǎn)進(jìn)去了,看到了實(shí)現(xiàn)方法的代碼如圖--
string轉(zhuǎn)json實(shí)現(xiàn)代碼 然后我繼續(xù)點(diǎn)進(jìn)去發(fā)現(xiàn)這個東西可以延伸出的是它上面的方法---



- 1.解釋意思:官方demo的解釋是這樣的--出口JSON兼容的模型作為一個字典對象
-
如上圖畫框框的部分,這個地方大神“葉孤城”是這樣寫到的
runtime實(shí)現(xiàn)KVC賦值
引用葉孤城話: Runtime相信大家都聽過。但是很少實(shí)踐過,我也沒怎么寫過runtime的東西,所以這里我也需要查一下資料?;蛘哒f換個思考角度,如果讓你寫這么一個庫,你怎么把JSON和你的model類型匹配呢?那第一步肯定是要獲取我model類型里的每一個attribute的名字然后通過KVC來賦值咯。怎么才能獲取一個NSObject類的屬性呢?
>如果這個地方你不知道什么叫做KVC那么請看下圖!!

關(guān)于他提出的這句話我去谷歌也做了百度搜索:搜索的解釋是這樣的---
- 官方解釋jsonModel的精髓就在于使用oc的反射機(jī)制!?。?!
- 那么講到這里我必須要說一下反射機(jī)制的問題-OC的反射機(jī)制
什么是反射機(jī)制呢?給大家舉個很簡單的例子:大家都寫過點(diǎn)擊事件吧,那么點(diǎn)擊事件里面的sel方法如圖所示--
按鈕點(diǎn)擊事件
其實(shí)這個按鈕點(diǎn)擊事件之所以能夠?qū)崿F(xiàn)也是因?yàn)榉瓷涞脑砦覀兺ㄟ^字符串方法來找到這個方法,從而實(shí)現(xiàn)這個方法,那么你以為反射機(jī)制就這么簡單嗎?其實(shí)不是的要想實(shí)現(xiàn)jsonModel實(shí)現(xiàn)映射來獲取到所有集成NSObject類里面屬性的原理遠(yuǎn)沒有這么簡單。其實(shí)她還用到了。runtime的原理。那么請看如下代碼----


- 備注 這個原理是什么意思呢。其實(shí)就是jsonModel的原理,我為了獲取到新建左側(cè)student里面的屬性值我就必須采用runtime原理。。
那么這里我們回到j(luò)sonModel源碼環(huán)節(jié),來看下jsonModel里面的代碼是怎樣實(shí)現(xiàn)映射環(huán)節(jié)的
*精髓 代碼如下--
//returns a list of the model's properties(返回一個列表的模型的屬性)
-(NSArray*)__properties__
{
//fetch the associated object(獲取相關(guān)對象)
NSDictionary* classProperties = objc_getAssociatedObject(self.class, &kClassPropertiesKey);
if (classProperties) return [classProperties allValues];
//if here, the class needs to inspect itself(如果在這里,需要檢查自己的類)
[self __setup__];
//return the property list(返回屬性列表)
classProperties = objc_getAssociatedObject(self.class, &kClassPropertiesKey);
return [classProperties allValues];
}
//inspects the class, get's a list of the class properties(檢查類,類的屬性列表)
-(void)__inspectProperties
{
//JMLog(@"Inspect class: %@", [self class]);
NSMutableDictionary* propertyIndex = [NSMutableDictionary dictionary];
//temp variables for the loops(臨時變量的循環(huán))
Class class = [self class];
NSScanner* scanner = nil;
NSString* propertyType = nil;
// inspect inherited properties up to the JSONModel class(檢查JSONModel類繼承的屬性)
while (class != [JSONModel class]) {
//JMLog(@"inspecting: %@", NSStringFromClass(class));
unsigned int propertyCount;
objc_property_t *properties = class_copyPropertyList(class, &propertyCount);
//loop over the class properties(遍歷類屬性)
for (unsigned int i = 0; i < propertyCount; i++) {
JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];
//get property name(獲取屬性名稱)
objc_property_t property = properties[i];
const char *propertyName = property_getName(property);
p.name = @(propertyName);
//JMLog(@"property: %@", p.name);
//get property attributes (獲取屬性)
const char *attrs = property_getAttributes(property);
NSString* propertyAttributes = @(attrs);
NSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@","];
//ignore read-only properties(忽略只讀屬性)
if ([attributeItems containsObject:@"R"]) {
continue; //to next property
}
//check for 64b BOOLs
if ([propertyAttributes hasPrefix:@"Tc,"]) {
//mask BOOLs as structs so they can have custom converters
p.structName = @"BOOL";
}
scanner = [NSScanner scannerWithString: propertyAttributes];
//JMLog(@"attr: %@", [NSString stringWithCString:attrs encoding:NSUTF8StringEncoding]);
[scanner scanUpToString:@"T" intoString: nil];
[scanner scanString:@"T" intoString:nil];
//check if the property is an instance of a class(檢查這個屬性是一個類的實(shí)例)
if ([scanner scanString:@"@\"" intoString: &propertyType]) {
[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]
intoString:&propertyType];
//JMLog(@"type: %@", propertyClassName);
p.type = NSClassFromString(propertyType);
p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound);
p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];
//read through the property protocols(讀取協(xié)議)
while ([scanner scanString:@"<" intoString:NULL]) {
NSString* protocolName = nil;
[scanner scanUpToString:@">" intoString: &protocolName];
if ([protocolName isEqualToString:@"Optional"]) {
p.isOptional = YES;
} else if([protocolName isEqualToString:@"Index"]) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
p.isIndex = YES;
#pragma GCC diagnostic pop
objc_setAssociatedObject(
self.class,
&kIndexPropertyNameKey,
p.name,
OBJC_ASSOCIATION_RETAIN // This is atomic
);
} else if([protocolName isEqualToString:@"Ignore"]) {
p = nil;
} else {
p.protocol = protocolName;
}
[scanner scanString:@">" intoString:NULL];
}
}
//check if the property is a structure(檢查屬性是一個結(jié)構(gòu))
else if ([scanner scanString:@"{" intoString: &propertyType]) {
[scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]
intoString:&propertyType];
p.isStandardJSONType = NO;
p.structName = propertyType;
}
//the property must be a primitive(屬性必須是原始的)
else {
//the property contains a primitive data type(屬性包含一個原始數(shù)據(jù)類型)
[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","]
intoString:&propertyType];
//get the full name of the primitive type(得到完整的原始類型的名稱)
propertyType = valueTransformer.primitivesNames[propertyType];
if (![allowedPrimitiveTypes containsObject:propertyType]) {
//type not allowed - programmer mistaken -> exception
@throw [NSException exceptionWithName:@"JSONModelProperty type not allowed"
reason:[NSString stringWithFormat:@"Property type of %@.%@ is not supported by JSONModel.", self.class, p.name]
userInfo:nil];
}
}
NSString *nsPropertyName = @(propertyName);
if([[self class] propertyIsOptional:nsPropertyName]){
p.isOptional = YES;
}
if([[self class] propertyIsIgnored:nsPropertyName]){
p = nil;
}
Class customClass = [[self class] classForCollectionProperty:nsPropertyName];
if (customClass) {
p.protocol = NSStringFromClass(customClass);
}
//few cases where JSONModel will ignore properties automatically(一些情況下,JSONModel將自動忽略屬性)
if ([propertyType isEqualToString:@"Block"]) {
p = nil;
}
//add the property object to the temp index(添加臨時指數(shù)的屬性對象)
if (p && ![propertyIndex objectForKey:p.name]) {
[propertyIndex setValue:p forKey:p.name];
}
// generate custom setters and getter(生成定制的setter和getter)
if (p)
{
NSString *name = [p.name stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[p.name substringToIndex:1].uppercaseString];
// getter
SEL getter = NSSelectorFromString([NSString stringWithFormat:@"JSONObjectFor%@", name]);
if ([self respondsToSelector:getter])
p.customGetter = getter;
// setters
p.customSetters = [NSMutableDictionary new];
SEL genericSetter = NSSelectorFromString([NSString stringWithFormat:@"set%@WithJSONObject:", name]);
if ([self respondsToSelector:genericSetter])
p.customSetters[@"generic"] = [NSValue valueWithBytes:&genericSetter objCType:@encode(SEL)];
for (Class type in allowedJSONTypes)
{
NSString *class = NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:type]);
if (p.customSetters[class])
continue;
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@With%@:", name, class]);
if ([self respondsToSelector:setter])
p.customSetters[class] = [NSValue valueWithBytes:&setter objCType:@encode(SEL)];
}
}
}
free(properties);
//ascend to the super of the class(提升的超類)
//(will do that until it reaches the root class - JSONModel)((將這樣做,直到它到達(dá)根類- JSONModel))
class = [class superclass];
}
//finally store the property index in the static property index(最后存儲屬性指標(biāo)的靜態(tài)屬性指標(biāo))
objc_setAssociatedObject(
self.class,
&kClassPropertiesKey,
[propertyIndex copy],
OBJC_ASSOCIATION_RETAIN // This is atomic
);
}
好,這邊大家看了上面密密麻麻的代碼,那我這邊需要對上述代碼做一下總結(jié),其實(shí)送的來說分為以下幾步。
- 1 通過調(diào)用自身的class方法獲取當(dāng)前類的元數(shù)據(jù)信息
- 2 通過runtime的 class_copyPropertyList 方法取得當(dāng)前類的屬性列表,以指針數(shù)組的形式返回
- 3 遍歷指針數(shù)組,通過property_getName獲取屬性名,property_getAttributes獲取屬性類型
- 4 使用NSScanner來掃描屬性類型字符串,將類似如下的形式"T@"NSNumber",&,N,V_id",處理成NSNumber,逐個屬性循環(huán)處理
- 5 將所有處理好的數(shù)據(jù)放入propertyIndex這個字典中
- 6通過objc_setAssociatedObject將這些數(shù)據(jù)關(guān)聯(lián)到kClassPropertiesKey
然后再通過使用時在properties方法中這樣取出屬性數(shù)據(jù):
使用JsonModel總結(jié)好處以及原理--
*1. JsonModel實(shí)現(xiàn)了怎樣把Json數(shù)據(jù)和你的model進(jìn)行類型匹配,同樣實(shí)現(xiàn)了獲取model里面的每一個屬性(attribute)然后通過KVC來進(jìn)行賦值
2.對于繁多的數(shù)據(jù)結(jié)構(gòu),我們只需要聲明model的協(xié)議(protocol),框架會幫你自動處理的。相比原本繁多的KVC賦值更加簡便的實(shí)現(xiàn)了json數(shù)據(jù)獲取和賦值,以及入?yún)⒑头磪⒌膶?shí)現(xiàn)功能。
3.JsonModel的原理其實(shí)就是利用了OC反射機(jī)制和runTime的原理實(shí)現(xiàn)的,喜歡的小伙伴也可以進(jìn)行百度查詢,本人簡書也會更新文章對反射機(jī)制和runTime進(jìn)行詳細(xì)解釋
本人個人微信公眾號地址(喜歡記得關(guān)注??)




