"小白"学JsonModel

目录
  • 1 JsonModel的简介以及XML简介
  • 2 JsonModel的入参与出参( NSString NSData Dictionary 与json数据相互转换)
  • 3 jsonModel原理 (如果想详细了解原理请先看下面两个文章)
    KVC与KVO
    OC的反射机制
依照习惯我们还是先介绍下jsonModel到底是个什么东东呢?
  • 答:IOS设备时常需要和服务器----一般来说是远程服务器进行数据交换。轻量级的APP应用只需少量的数据交互,例如每隔几分钟获取一次是否有更新的消息。另一些APP应用则需要与后台服务器有类似分享信息,阅读关注,上传一张照片等操作这里就存在了交互的问题,交互分为两种

(这里下面会对两种交互进行讲解json转换model以及string/字典/NSData转换json进行详细解答)

用XML来发送这些信息给Web服务器已经过时了。越来越多的移动应用更倾向于用JSON这种数据格式。一旦计划开发移动应用的并有与后台通信的需求,则要用JSON数据格式与服务器通信互相通讯。

JsonModel是用Objective-C写的开源库。它包含了接受发送、解析JSON数据,用JSON数据驱动初始化你的类,还能够检验JSON和嵌套模型等功能。

  • (为什么说xml过时了呢?首先我们看下xml格式的代码) xml数据展示
<?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数据展示
{
"number":"啦啦啦啦啦", 
"name":"啦啦啦啦啦",
"age": 1000
}
  • 1.是不是看着就比json代码乱。

  • 2.其次xml代码这边需要用SAX和DOM或者TBXML, TouchXML, KissXML, TinyXML框架进行解析,然后将解析之后的数据进行拼接,拼接之后最后两段的处理还需要还原成json数据进行字典处理,所以现在xml数据慢慢已经被淡化了。

    那么下面我们言归正传还是对jsonmodel进行分析

  • 上面说到我们常见的两种形式就是将json数据转换成模型model的数据或者将string数据转换为json的数据,以及字典转json数据再或者Data转json,那么其实这两种情况就是入参和反参的两种形式(json转换为其他形式叫做出参,其他形式转换为json叫做入参)!

  • 入参eg:也就是指我们常说的给后台人员数据,我们需要把一些数据进行拼接转换成json串的形式传递给后台人员然后他自己在进行解析,如果你还是没搞懂那么看下图--

    入参string转json
  • 好看完图我们去看看jsonModel源码里面写的string转换json的方法以及字典转json或者是NSData转json的实现--如图--

string/字典/NSData转换json
  • 之后我又点进去了,看到了实现方法的代码如图--


    string转json实现代码
  • 然后我继续点进去发现这个东西可以延伸出的是它上面的方法---

探索jsonModel
探索jsonmodel
字典转json
  • 1.解释意思:官方demo的解释是这样的--出口JSON兼容的模型作为一个字典对象
  • 如上图画框框的部分,这个地方大神“叶孤城”是这样写到的


    runtime实现KVC赋值

引用叶孤城话: Runtime相信大家都听过。但是很少实践过,我也没怎么写过runtime的东西,所以这里我也需要查一下资料。或者说换个思考角度,如果让你写这么一个库,你怎么把JSON和你的model类型匹配呢?那第一步肯定是要获取我model类型里的每一个attribute的名字然后通过KVC来赋值咯。怎么才能获取一个NSObject类的属性呢?

 >如果这个地方你不知道什么叫做KVC那么请看下图!!
KVC图解

关于他提出的这句话我去谷歌也做了百度搜索:搜索的解释是这样的---

  • 官方解释jsonModel的精髓就在于使用oc的反射机制!!!!
  • 那么讲到这里我必须要说一下反射机制的问题-OC的反射机制
    什么是反射机制呢?给大家举个很简单的例子:大家都写过点击事件吧,那么点击事件里面的sel方法如图所示--
    按钮点击事件

    其实这个按钮点击事件之所以能够实现也是因为反射的原理我们通过字符串方法来找到这个方法,从而实现这个方法,那么你以为反射机制就这么简单吗?其实不是的要想实现jsonModel实现映射来获取到所有集成NSObject类里面属性的原理远没有这么简单。其实她还用到了。runtime的原理。那么请看如下代码----
映射原理

映射原理
  • 备注 这个原理是什么意思呢。其实就是jsonModel的原理,我为了获取到新建左侧student里面的属性值我就必须采用runtime原理。。
那么这里我们回到jsonModel源码环节,来看下jsonModel里面的代码是怎样实现映射环节的

*精髓 代码如下--

//returns a list of the model's properties(返回一个列表的模型的属性)
-(NSArray*)__properties__
{
    //fetch the associated object(获取相关对象)
    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(临时变量的循环)
    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(检查这个属性是一个类的实例)
            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(读取协议)
                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(检查属性是一个结构)
            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(属性包含一个原始数据类型)
                [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(添加临时指数的属性对象)
            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)((将这样做,直到它到达根类- JSONModel))
        class = [class superclass];
    }

    //finally store the property index in the static property index(最后存储属性指标的静态属性指标)
    objc_setAssociatedObject(
                             self.class,
                             &kClassPropertiesKey,
                             [propertyIndex copy],
                             OBJC_ASSOCIATION_RETAIN // This is atomic
                             );
}


好,这边大家看了上面密密麻麻的代码,那我这边需要对上述代码做一下总结,其实送的来说分为以下几步。
  • 1 通过调用自身的class方法获取当前类的元数据信息
  • 2 通过runtime的 class_copyPropertyList 方法取得当前类的属性列表,以指针数组的形式返回
  • 3 遍历指针数组,通过property_getName获取属性名,property_getAttributes获取属性类型
  • 4 使用NSScanner来扫描属性类型字符串,将类似如下的形式"T@"NSNumber",&,N,V_id",处理成NSNumber,逐个属性循环处理
  • 5 将所有处理好的数据放入propertyIndex这个字典中
  • 6通过objc_setAssociatedObject将这些数据关联到kClassPropertiesKey

然后再通过使用时在properties方法中这样取出属性数据:

使用JsonModel总结好处以及原理--

*1. JsonModel实现了怎样把Json数据和你的model进行类型匹配,同样实现了获取model里面的每一个属性(attribute)然后通过KVC来进行赋值
2.对于繁多的数据结构,我们只需要声明model的协议(protocol),框架会帮你自动处理的。相比原本繁多的KVC赋值更加简便的实现了json数据获取和赋值,以及入参和反参的实现功能。
3.JsonModel的原理其实就是利用了OC反射机制和runTime的原理实现的,喜欢的小伙伴也可以进行百度查询,本人简书也会更新文章对反射机制和runTime进行详细解释

本人个人微信公众号地址(喜欢记得关注😯)


辛小二个人微信公众号地址
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,058评论 4 62
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,732评论 25 707
  • 情绪管理果真是门艺术,不断地经历,还可能不断地失控。 遇到两件小事情, 其一 产品有一个活动,需要每个月做特殊配置...
    子言juanita阅读 392评论 10 2
  • 文/老葫芦 (2017年4月21日) 直至援藏来到阿里 我才体会生命 在繁忙后的意义 大半生的路 颠沛接着流离 路...
    老葫芦阅读 355评论 0 0
  • 今天早上去儿童医院志愿服务,护士长交给我了一个不太容易的任务——去重症监护室外做问卷调查。 吸了口气我就开始了,监...
    南无NAMO阅读 188评论 0 0