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