MJExtension的用法优化

关于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

版权声明:本文为博主原创文章,未经博主允许不得转载。
最后欢迎大家关注文明的iOS开发公众号:
方式1、搜索:“iOS开发by文明”
方式2、扫描下方二维码
这里写图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值