MJExtension的用法优化

作者: coderST | 来源:发表于2017-08-30 09:10 被阅读895次

    关于MJExtension的基本用法和高级用法大家都熟透在心了,很多文章介绍MJExtension怎么用,介绍MJExtension的高级API,泛滥了哦!一发不可收拾!所以此文主要介绍优化。

    • 本人找了好多文章,没有谈及MJExtension的优化的。MJ老师也在github上MJExtension的demo里做了用法引导,大家都用的很happy!
      但是我有一个疑问,这个疑问大部分用MJExtension的开发者应该都遇到过,就是关于每个model的配置代码应该写在哪里?!可能是因为MJExtension官方已经给了引导用法,大部分开发者直接照搬了MJExtension的demo中的用法。关于model需要配置的东东这里举个例子,比如后台返回的json里面含有id、void、description、new等OC关键字的时候,需要进行model属性的key的替换,意思就是用ID当key去map映射json中的id这个key,就可以用这个方法mj_setupReplacedKeyFromPropertyName进行配置,或者在Student类的.m中重写+mj_replacedKeyFromPropertyName这个方法,代码如下:
    // How to map
    [Student mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
        return @{
                   @"ID" : @"id",
                   @"desc" : @"desciption"
               };
    }];
    // Equals: Student.m implements +mj_replacedKeyFromPropertyName method.
    
    • 还有一个比较蛋疼的问题,MJExtension也可以方便的设置,就是后台返回的数据中表示一个对象的其中的一个key不固定,举个例子,老师的id这个key,其实表示的意义是一样的,就是老师的编号id,但是由于后台开发人员比较多,没有互相交流和查看对方的代码,导致A接口返回的key是id,B接口tid,C接口ID,D接口Id,E接口iD。这就很尴尬了,本人真实遇到过,想让后台改过来,但是呢?他们说这样改的工作量相当相当大……….,此处省略一万字

    那么解决办法就是这样的,我model中用teacherId这个key去map后台所有可能的key

    [MJStudent mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
                return @{@"teacherId" : @[@"id", @"tid",@"ID",@"iD",@"Id"]};
            }];
    
    • 最后还有一个需要设置的项,就是model中含有数组,这个数组为一个model,下面是MJ老师给的例子

    Model contains model-array【模型中有个数组属性,数组里面又要装着其他模型】

    @interface Ad : NSObject
    @property (copy, nonatomic) NSString *image;
    @property (copy, nonatomic) NSString *url;
    @end
    
    @interface StatusResult : NSObject
    /** Contatins status model */
    @property (strong, nonatomic) NSMutableArray *statuses;
    /** Contatins ad model */
    @property (strong, nonatomic) NSArray *ads;
    @property (strong, nonatomic) NSNumber *totalNumber;
    @end
    
    /***********************************************/
    
    // Tell MJExtension what type model will be contained in statuses and ads.
    [StatusResult mj_setupObjectClassInArray:^NSDictionary *{
        return @{
                   @"statuses" : @"Status",
                   // @"statuses" : [Status class],
                   @"ads" : @"Ad"
                   // @"ads" : [Ad class]
               };
    }];
    // Equals: StatusResult.m implements +mj_objectClassInArray method.
    
    NSDictionary *dict = @{
        @"statuses" : @[
                          @{
                              @"text" : @"Nice weather!",
                              @"user" : @{
                                  @"name" : @"Rose",
                                  @"icon" : @"nami.png"
                              }
                          },
                          @{
                              @"text" : @"Go camping tomorrow!",
                              @"user" : @{
                                  @"name" : @"Jack",
                                  @"icon" : @"lufy.png"
                              }
                          }
                      ],
        @"ads" : @[
                     @{
                         @"image" : @"ad01.png",
                         @"url" : @"http://www.ad01.com"
                     },
                     @{
                         @"image" : @"ad02.png",
                         @"url" : @"http://www.ad02.com"
                     }
                 ],
        @"totalNumber" : @"2014"
    };
    
    // JSON -> StatusResult
    StatusResult *result = [StatusResult mj_objectWithKeyValues:dict];
    
    NSLog(@"totalNumber=%@", result.totalNumber);
    // totalNumber=2014
    
    // Printing
    for (Status *status in result.statuses) {
        NSString *text = status.text;
        NSString *name = status.user.name;
        NSString *icon = status.user.icon;
        NSLog(@"text=%@, name=%@, icon=%@", text, name, icon);
    }
    // text=Nice weather!, name=Rose, icon=nami.png
    // text=Go camping tomorrow!, name=Jack, icon=lufy.png
    
    // Printing
    for (Ad *ad in result.ads) {
        NSLog(@"image=%@, url=%@", ad.image, ad.url);
    }
    // image=ad01.png, url=http://www.ad01.com
    // image=ad02.png, url=http://www.ad02.com
    
    • 其中设置的关键代码是这句
    [StatusResult mj_setupObjectClassInArray:^NSDictionary *{
        return @{
                   @"statuses" : @"Status",
                   // @"statuses" : [Status class],
                   @"ads" : @"Ad"
                   // @"ads" : [Ad class]
               };
    }];
    // Equals: StatusResult.m implements +mj_objectClassInArray method.
    
    • 所有以上举的例子都有设置(配置)这些代码,开发中你写在哪里了呢?直接写在VC里面?写在model类的.m实现文件里面?还是用了MJ老师的MJExtensionConfig类,一股脑全部放到MJExtensionConfig的.m文件里面,而且是写在MJExtensionConfig.m的+load方法里面。

    • 我个人觉得都不是最优的方案,经过我个人的研究,经历和尝试了下面几种方案如下,欢迎大家留言批评指正

    方案一、

    • 这些配置代码写VC里面显然不对,model都是开发组的成员可复用的类,一个model类可能出现在n多个VC里面,那么如果我的一个model被我同事拿去用,他明显要在VC里面写这些配置代码,事实证明的确如此,他的确是写了一遍,产生了大量的垃圾代码。也不符合封装的逻辑,他错了,我也错了,因为我没封装好给别人直接使用。

    方案二、

    • 在每个model的.m中重写方法,返回要配置的东东。这样再也不怕其他同事用你的model了,因为你都配置好了,他直接用就可以了,垃圾代码也少了,貌似是最优的,也符合封装的概念,但是,但是,但是,我发现重写的这些方法会反复调用,次数非常多,违背了初衷,本来设置的东东,一次设置,终身使用才行啊,尼玛一个json转model的解析过程调用n多次这个方法,不合理啊。到这里我也否认了这个方案。

    方案三、

    • 既然方案一和方案二都不好,我就直接下载MJ老是的Demo,看MJ老师是怎么用的,结果眼前一亮,MJ老师果然是高,实在是高,他直接建立一个类MJExtensionConfig,然后把项目中所有的model的配置都放到了MJExtensionConfig的.m中的+load方法中,而且标明了这样的话,copy部分贴在下面方便查看。
    #import "MJExtensionConfig.h"
    #import "MJExtension.h"
    #import "MJBag.h"
    #import "MJUser.h"
    #import "MJStatusResult.h"
    #import "MJStudent.h"
    #import "MJDog.h"
    #import "MJBook.h"
    
    @implementation MJExtensionConfig
    /**
     *  这个方法会在MJExtensionConfig加载进内存时调用一次
     */
    + (void)load
    {
    #pragma mark 如果使用NSObject来调用这些方法,代表所有继承自NSObject的类都会生效
    #pragma mark NSObject中的ID属性对应着字典中的id
        [NSObject mj_setupReplacedKeyFromPropertyName:^NSDictionary *{
            return @{
                     @"ID" : @"id"
                     };
        }];
    
    
    
    #pragma mark MJUser类的只有name、icon属性参与字典转模型
    //    [MJUser mj_setupAllowedPropertyNames:^NSArray *{
    //        return @[@"name", @"icon"];
    //    }];
        // 相当于在MJUser.m中实现了+(NSArray *)mj_allowedPropertyNames方法
    
    #pragma mark MJBag类中的name属性不参与归档
        [MJBag mj_setupIgnoredCodingPropertyNames:^NSArray *{
            return @[@"name"];
        }];
        // 相当于在MJBag.m中实现了+(NSArray *)mj_ignoredCodingPropertyNames方法
    
    #pragma mark MJBag类中只有price属性参与归档
    //    [MJBag mj_setupAllowedCodingPropertyNames:^NSArray *{
    //        return @[@"price"];
    //    }];
        // 相当于在MJBag.m中实现了+(NSArray *)mj_allowedCodingPropertyNames方法
    
    • 大家都知道+load方法在开发者不主动调用的情况下,如果你实现了load方法,那么只会在APP启动应用的时候调用一次,而且是在main函数被调用之前调用,算是比较早调用的func,load会把项目中所有的类都加载load一遍,load方法貌似可以进行项目中model类的配置,好像是再合适不过的了。但是我否定了方案三、也就是MJ老师的方案或者说是引导用法。为什么呢?load方法会拖慢程序启动时间,写demo可以,就如MJ老师的demo,但是写项目不可以,他会拖慢启动时间,这个是我所不能忍受的,另外一个不好之处就是MJExtensionConfig文件中要import项目中大部分需要设置model参数的类文件,这样不太好,项目越来越大,MJExtensionConfig中导入的头文件越来越多。 啊啊啊,我受不了。推荐一个cocoaChina的文章《iOS APP启动优化》http://www.cocoachina.com/ios/20170731/20071.html 摘抄一部分相关内容如下:优化方案
      main()调用之前加载过程,优化内容
      减少framework引用删除无用类,无用函数减少+load 函数使用

    方案四、

    • 方案四是我个人开发中研究出的一个方案,是最优的方案。其实很简单,就是放到每个model类的.m中的+initialize方法中。为啥最优,为啥你要否定MJ的方案,解释如下,
      • 1、因为写在每个类的.m中,符合封装的概念,其他的同事直接用我的model类,不用管配置,每个model类管理自己的配置,算是比较合理的,如果有一天不需要这个类了,直接删除,VC里面相应的改动比较少,也体现了整体的概念。
      • 2、+initialize和+load方法有不同,initialize方法不会在程序启动的时候调用,而且有lazy懒加载的感觉, initialize方法相关要点

    initialize的自然调用是在第一次主动使用当前类的时候(lazy,这一点和Java类的“clinit”的很像)。在initialize方法收到调用时,运行环境基本健全。initialize的运行过程中是能保证线程安全的。和load不同,即使子类不实现initialize方法,会把父类的实现继承过来调用一遍。注意的是在此之前,父类的方法已经被执行过一次了,同样不需要super调用。由于initialize的这些特点,使得其应用比load要略微广泛一些。可用来做一些初始化工作,或者单例模式的一种实现方案。

    • initialize的这些特点决定了方案四是目前最优的方案,不会拖慢启动时间,懒加载的模式会在我们第一次使用model类的时候,设置一次,使用则设置,不使用则不设置,一旦设置,对于APP这次启动而言,相关设置终身有效。如果是model中有继承关系,则在基类里面配置,因为initialize和load不同,即使子类不实现initialize方法,会把父类的实现继承过来调用一遍。注意的是在此之前,父类的方法已经被执行过一次了,注意initialize和load不要调用super哦!MJ老师的MJExtensionConfig这个方案,如果大家用了,其实很不方便,如果需要改动,那么就要去到这个MJExtensionConfig里面找你的model,然后改动(删、添加等),还有就是如果同事想看你的model的配置,找不到,因为可能他不知道有这个MJExtensionConfig文件,但是,如果你直接写在model的.m中,那么同事也很方便查看你的代码。还有就是可能你的同事添加了相同model的配置代码到MJExtensionConfig文件中,不会报错(写.m中的initialize不会产生垃圾代码,重复func会报错),产生垃圾代码,当然这个情况比较少,也是因为那个开发者不长眼。哈哈哈!本人已经这样使用,效果非常好,如果你的项目中使用了MJExtension我想你是时候优化一下了。
      参考文章《NSObject的load和initialize方法》http://www.cocoachina.com/ios/20150104/10826.html
      原文地址:http://blog.csdn.net/wenmingzheng/article/details/77610196

    相关文章

      网友评论

        本文标题:MJExtension的用法优化

        本文链接:https://www.haomeiwen.com/subject/fcszdxtx.html