"小白"學(xué)JsonModel

目錄
  • 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)--如圖--

string/字典/NSData轉(zhuǎn)換json
  • 之后我又點(diǎn)進(jìn)去了,看到了實(shí)現(xiàn)方法的代碼如圖--


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

探索jsonModel
探索jsonmodel
字典轉(zhuǎn)json
  • 1.解釋意思:官方demo的解釋是這樣的--出口JSON兼容的模型作為一個字典對象
  • 如上圖畫框框的部分,這個地方大神“葉孤城”是這樣寫到的


    runtime實(shí)現(xiàn)KVC賦值

引用葉孤城話: Runtime相信大家都聽過。但是很少實(shí)踐過,我也沒怎么寫過runtime的東西,所以這里我也需要查一下資料?;蛘哒f換個思考角度,如果讓你寫這么一個庫,你怎么把JSON和你的model類型匹配呢?那第一步肯定是要獲取我model類型里的每一個attribute的名字然后通過KVC來賦值咯。怎么才能獲取一個NSObject類的屬性呢?

 >如果這個地方你不知道什么叫做KVC那么請看下圖!!
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)注??)


辛小二個人微信公眾號地址
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,045評論 4 61
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,765評論 25 709
  • 情緒管理果真是門藝術(shù),不斷地經(jīng)歷,還可能不斷地失控。 遇到兩件小事情, 其一 產(chǎn)品有一個活動,需要每個月做特殊配置...
    子言juanita閱讀 470評論 10 2
  • 文/老葫蘆 (2017年4月21日) 直至援藏來到阿里 我才體會生命 在繁忙后的意義 大半生的路 顛沛接著流離 路...
    老葫蘆閱讀 415評論 0 0
  • 今天早上去兒童醫(yī)院志愿服務(wù),護(hù)士長交給我了一個不太容易的任務(wù)——去重癥監(jiān)護(hù)室外做問卷調(diào)查。 吸了口氣我就開始了,監(jiān)...
    南無NAMO閱讀 239評論 0 0

友情鏈接更多精彩內(nèi)容