-
一、MJExtension第三方框架
我们在iOS开发过程中,我们常常需要将字典数据(也就是JSON数据)与Model模型之间的转化,例如网络请求返回的微博数据、等等,如果我们自己全部手动去创建模型并赋值,都是一些毫无技术含量的代码,费时费力,而且还可能会赋值出错,让我们很头疼。MJExtension
框架就是为了解决这个问题而设计得第三方开源库。这个开源库是之前传智博客的讲师李明杰老师写的,现在他自己出来做了,我iOS入门都是看李明杰老师的培训视频学习的,他讲得非常好,我非常喜欢他,他也算是我的老师了,他的作品我还是要学习下的。
提供了以下的一些方法实现: 简单的字典 –> 模型 JSON字符串 –> 模型 复杂的字典 –> 模型 (模型里面包含了模型) 复杂的字典 –> 模型 (模型的数组属性里面又装着模型) 复杂的字典 –> 模型(模型属性名和字典的key不一样) 字典数组 –> 模型数组 模型 –> 字典 模型数组 –> 字典数组 字典 –> CoreData模型 归档与解档NSCoding 过滤字典的值
框架是利用Obj-C的运行时机制编写的,现在iOS开发语言往Swift语言发展,我不太清楚Swift语言是否也有这种特性,该框架以后会不会在Swift语言上也发展下去不得而知,不过这个框架很轻量级,非常适合初级开发者去看它的源码,对理解Obj-C的运行时机制有非常大的帮助。MJExtension
二、Runtime运行时机制简单了解
Runtime
简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制。
OC的函数调用类似于消息发送,属于动态调用过程。在编译的时候并不能决定真正调用哪个函数。事实证明,在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错。而C语言在编译阶段就会报错。只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
例如,下面的这个代码在编译时会被转化: ?
iOS的顶层基类NSObject含有一个指向objc_class结构体的isa指针: ?1 2 3 4 <code
class
=
"language-objc hljs scss"
>
[obj makeTest];
objc_msgSend(obj,
@selector
(makeText));</code>
在1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <code
class
=
"language-objc hljs d"
>
@interface
NSObject{
Class isa;
};
typedef struct objc_class *Class;
struct objc_class {
Class isa;
// 指向metaclass,也就是静态的Class
Class super_class ;
// 指向其父类
const
char
*name ;
// 类名
long
version ;
// 类的版本信息,初始化默认为0
long
info;
long
instance_size ;
// 该类的实例变量大小(包括从父类继承下来的实例变量);
struct objc_ivar_list *ivars;
// 用于存储每个成员变量的地址
struct objc_method_list **methodLists ;
struct objc_cache *cache;
// 指向最近使用的方法的指针,用于提升效率;
struct objc_protocol_list *protocols;
// 存储该类遵守的协议
};</code>
函数的调用过程: 首先通过obj的isa指针找到obj对应的Class。 在Class中先去objc_msgSend
中通过SEL查找对应函数cache
若method
中未找到,再去cache
中查找 若methodLists
中未找到,则进入methodLists
按前面的步骤进行递归查找 若找到superClass
,则将method
加入到method
中,以方便下次查找,并通过cache
中的函数指针跳转到对应的函数中去执行。 如果一直查找到method
还没查找到,则会进入消息动态处理流程。 消息动态处理流程: ?NSObject
日本读研究生 app开发报价单 蓝鸥ios培训 机械键盘排行榜1 2 3 4 5 6 7 8 <code
class
=
"language-objc hljs markdown"
>
+ (BOOL)resolveInstanceMethod:(SEL)sel;
- (id)forwardingTargetForSelector:(SEL)aSelector;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
- (
void
)forwardInvocation:(NSInvocation *)anInvocation;</code>
什么是前端开发
所以使用Runtime机制我们就可以动态向类添加方法或属性: ?
1 2 3 4 5 6 7 8 9 10 11 12 <code
class
=
"language-objc hljs objectivec"
>
class_addIvar(kclass,
"expression"
, size, alignment,
"*"
);
class_addMethod(kclass,
@selector
(setExpressionFormula:), (IMP)setExpressionFormula,
"v@:@"
);
class_addMethod(kclass,
@selector
(getExpressionFormula), (IMP)getExpressionFormula,
"@@:"
);
static
void
setExpressionFormula(id self, SEL cmd, id value){
NSLog(@
"call setExpressionFormula"
);
}
static
id getExpressionFormula(id self, SEL cmd){
NSLog(@
"call getExpressionFormula"
);
return
nil;
}</code>
表示void,v
表示id类型,@
表示SEL类型:
:表示返回值为void,接受一个id类型、一个SEL类型、一个id类型的方法"v@:@"
"@@:"
:表示返回值为id类型,接受一个id类型和一个SEL类型参数的方法
具体Runtime运行时使用细节,这里就不细讲,只是简单了解下Runtime是可以做到动态向类添加属性和方法就行。
三、MJExtension使用
的大部分方法实现都集成到了分类上,不需要使用新的类,只需要包含头文件MJExtension
即可。MJExtension.h
在github上的使用说明已经写得十分明白了。MJExtension
1. 简单的字典 –> 模型
模型类User定义: ?使用实例: ?1 2 3 4 5 6 7 8 9 10 11 12 13 <code
class
=
"language-objc hljs objectivec"
>typedef
enum
{
SexMale,
SexFemale
} Sex;
@interface
User : NSObject
@property
(copy, nonatomic) NSString *name;
@property
(copy, nonatomic) NSString *icon;
@property
(assign, nonatomic) unsigned
int
age;
@property
(copy, nonatomic) NSString *height;
@property
(strong, nonatomic) NSNumber *money;
@property
(assign, nonatomic) Sex sex;
@property
(assign, nonatomic, getter=isGay) BOOL gay;
@end
</code>
1 2 3 4 5 6 7 8 9 10 11 <code
class
=
"language-objc hljs ruby"
>NSDictionary *dict = @{
@
"name"
: @
"Jack"
,
@
"icon"
: @
"lufy.png"
,
@
"age"
:
@20
,
@
"height"
: @
"1.55"
,
@
"money"
:
@100
.9,
@
"sex"
: @(SexFemale),
@
"gay"
: @
"NO"
};
//字典转模型,使用的是mj_objectWithKeyValues:方法
User *user = [User mj_objectWithKeyValues:dict];</code>
2. JSON字符串 –> 模型
使用实例: ?1 2 3 4 5 <code
class
=
"language-objc hljs tex"
>
// 定义一个JSON字符串
NSString*jsonString = @
"{\"name\":\"Jack\", \"icon\":\"lufy.png\", \"age\":20}"
;
// JSON字符串转模型
User *user = [User mj_objectWithKeyValues:jsonString];
</code>
3. 复杂的字典 –> 模型 (模型里面包含了模型)
模型类Status定义: ?使用实例: ?1 2 3 4 5 <code
class
=
"language-objc hljs objectivec"
>
@interfaceStatus
: NSObject
@property
(copy, nonatomic) NSString*text;
@property
(strong, nonatomic) User *user;
@property
(strong, nonatomic) Status *retweetedStatus;
@end
</code>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <code
class
=
"language-objc hljs avrasm"
>NSDictionary *dict = @{
@
"text"
: @
"Agree!Nice weather!"
,
@
"user"
: @{
@
"name"
: @
"Jack"
,
@
"icon"
: @
"lufy.png"
},
@
"retweetedStatus"
: @{
@
"text"
: @
"Nice weather!"
,
@
"user"
: @{
@
"name"
: @
"Rose"
,
@
"icon"
: @
"nami.png"
}
}
};
//字典转模型,模型里面含有模型
Status *status = [Status mj_objectWithKeyValues:dict];
NSString *text = status.text;
NSString *name = status.user.name;
NSString *icon = status.user.icon;
NSLog(@
"text=%@, name=%@, icon=%@"
, text, name, icon);
// text=Agree!Nice weather!, name=Jack, icon=lufy.png
NSString *text2 = status.retweetedStatus.text;
NSString *name2 = status.retweetedStatus.user.name;
NSString *icon2 = status.retweetedStatus.user.icon;
NSLog(@
"text2=%@, name2=%@, icon2=%@"
, text2, name2, icon2);
// text2=Nice weather!, name2=Rose, icon2=nami.png</code>
4. 复杂的字典 –> 模型 (模型的数组属性里面又装着模型)
模型类Ad和StatusResult定义: ?使用实例: ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <code
class
=
"language-objc hljs java"
>
@interfaceAd
: NSObject
@property
(copy, nonatomic) NSString*image;
@property
(copy, nonatomic) NSString*url;
@end
@interfaceStatusResult
: NSObject
@property
(strong, nonatomic) NSMutableArray*statuses;
@property
(strong, nonatomic) NSArray*ads;
@property
(strong, nonatomic) NSNumber*totalNumber;
@end
#
import
"MJExtension.h"
@implementation
StatusResult
+ (NSDictionary *)mj_ objectClassInArray{
return
@{ @
"statuses"
: @
"Status"
,
@
"ads"
: @
"Ad"
};
}
@end
</code>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 <code
class
=
"language-objc hljs avrasm"
>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"
};
//字典转模型,支持模型的数组属性里面又装着模型
StatusResult *result = [StatusResult mj_objectWithKeyValues:dict];
//打印博主信息
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
//打印广告
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</code>
5. 复杂的字典 –> 模型(模型属性名和字典的key不一样)
模型类Bag和Student定义: ?使用实例: ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <code
class
=
"language-objc hljs objectivec"
>
@interface
Bag : NSObject
@property
(copy, nonatomic) NSString *name;
@property
(assign, nonatomic)
double
price;
@end
@interface
Student : NSObject
@property
(copy, nonatomic) NSString *ID;
@property
(copy, nonatomic) NSString *desc;
@property
(copy, nonatomic) NSString *nowName;
@property
(copy, nonatomic) NSString *oldName;
@property
(copy, nonatomic) NSString *nameChangedTime;
@property
(strong, nonatomic) Bag *bag;
@end
#
import
"MJExtension.h"
@implementation
+ (NSDictionary *)mj_replacedKeyFromPropertyName{
return
@{
@
"ID"
: @
"id"
,
@
"desc"
: @
"desciption"
,
@
"oldName"
: @
"name.oldName"
,
@
"nowName"
: @
"name.newName"
,
@
"nameChangedTime"
: @
"name.info[1].nameChangedTime"
,
@
"bag"
: @
"other.bag"
};
}
@end
</code>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <code
class
=
"language-objc hljs avrasm"
>NSDictionary *dict = @{
@
"id"
: @
"20"
,
@
"desciption"
: @
"kids"
,
@
"name"
: @{
@
"newName"
: @
"lufy"
,
@
"oldName"
: @
"kitty"
,
@
"info"
: @[
@
"test-data"
,
@{
@
"nameChangedTime"
: @
"2013-08"
}
]
},
@
"other"
: @{
@
"bag"
: @{
@
"name"
: @
"a red bag"
,
@
"price"
:
@100
.7
}
}
};
//字典转模型,支持多级映射
Student *stu = [Student mj_objectWithKeyValues:dict];
//打印
NSLog(@
"ID=%@, desc=%@, oldName=%@, nowName=%@, nameChangedTime=%@"
,
stu.ID, stu.desc, stu.oldName, stu.nowName, stu.nameChangedTime);
// ID=20, desc=kids, oldName=kitty, nowName=lufy, nameChangedTime=2013-08
NSLog(@
"bagName=%@, bagPrice=%f"
, stu.bag.name, stu.bag.price);
// bagName=a red bag, bagPrice=100.700000</code>
6. 字典数组 –> 模型数组
使用实例: ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <code
class
=
"language-objc hljs objectivec"
>NSArray *dictArray = @[
@{
@
"name"
: @
"Jack"
,
@
"icon"
: @
"lufy.png"
},
@{
@
"name"
: @
"Rose"
,
@
"icon"
: @
"nami.png"
}
];
//字典数组转模型数组,使用的是mj_objectArrayWithKeyValuesArray:方法
NSArray *userArray = [User mj_objectArrayWithKeyValuesArray:dictArray];
//打印
for
(User *user in userArray) {
NSLog(@
"name=%@, icon=%@"
, user.name, user.icon);
}
// name=Jack, icon=lufy.png
// name=Rose, icon=nami.png</code>
7. 模型 –> 字典
使用实例: ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <code
class
=
"language-objc hljs avrasm"
>
//创建一个模型对象
User *user = [[User alloc] init];
user.name = @
"Jack"
;
user.icon = @
"lufy.png"
;
Status *status = [[Status alloc] init];
status.user = user;
status.text = @
"Nice mood!"
;
//模型转字典,使用的是mj_keyValues属性
NSDictionary*statusDict = status.mj_keyValues;
NSLog(@
"%@"
, statusDict);
</code>
8. 模型数组 –> 字典数组
使用实例: ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <code
class
=
"language-objc hljs objectivec"
>
//创建模型数组
User *user1 = [[User alloc] init];
user1.name = @
"Jack"
;
user1.icon = @
"lufy.png"
;
User *user2 = [[User alloc] init];
user2.name = @
"Rose"
;
user2.icon = @
"nami.png"
;
NSArray *userArray = @[user1, user2];
//模型数组转字典数组,使用的是mj_keyValuesArrayWithObjectArray:方法
NSArray *dictArray = [User mj_keyValuesArrayWithObjectArray:userArray];
NSLog(@
"%@"
, dictArray);
</code>
9. 字典 –> CoreData模型
使用实例: ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 <code
class
=
"language-objc hljs ruby"
>NSDictionary *dict = @{
@
"name"
: @
"Jack"
,
@
"icon"
: @
"lufy.png"
,
@
"age"
:
@20
,
@
"height"
:
@1
.55,
@
"money"
: @
"100.9"
,
@
"sex"
: @(SexFemale),
@
"gay"
: @
"true"
};
//字典转为CoreData模型
NSManagedObjectContext *context = nil;
User *user = [User mj_objectWithKeyValues:dict
context:context];
[context save:nil];</code>
10. 归档与解档NSCoding
模型类Bag添加实现: ?使用实例: ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <code
class
=
"language-objc hljs objectivec"
>
@interface
Bag : NSObject <nscoding>
@property
(copy, nonatomic) NSString *name;
@property
(assign, nonatomic)
double
price;
@end
#
import
"MJExtension.h"
@implementation
Bag
//添加了下面的宏定义
MJExtensionCodingImplementation
+ (NSArray *)mj_ignoredCodingPropertyNames{
return
@[@
"name"
];
}
@end
</nscoding></code>
1 2 3 4 5 6 7 8 9 10 11 12 <code
class
=
"language-objc hljs objectivec"
>
//创建模型
Bag *bag = [[Bag alloc] init];
bag.name = @
"Red bag"
;
bag.price =
200.8
;
//获取归档路径
NSString *file = [NSHomeDirectory() stringByAppendingPathComponent:@
"Desktop/bag.data"
];
//归档
[NSKeyedArchiver archiveRootObject:bag toFile:file];
//解档
Bag *decodedBag = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
NSLog(@
"name=%@, price=%f"
, decodedBag.name, decodedBag.price);
// name=(null), price=200.800000</code>
11. 过滤字典的值
模型类Book实现: ?使用实例: ?1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <code
class
=
"language-objc hljs objectivec"
>
@interface
Book: NSObject
@property
(copy, nonatomic) NSString *name;
@property
(strong, nonatomic) NSDate *publishedTime;
@end
#
import
"MJExtension.h"
@implementation
Book
- (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property
{
if
([property.name isEqualToString:@
"publisher"
]) {
if
(oldValue == nil) {
return
@
""
;
}
}
else
if
(property.type.typeClass == [NSDate
class
]) {
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @
"yyyy-MM-dd"
;
return
[fmt dateFromString:oldValue];
}
return
oldValue;
}
@end
</code>
1 2 3 4 5 6 7 8 <code
class
=
"language-objc hljs objectivec"
>NSDictionary *dict = @{
@
"name"
: @
"5分钟突破iOS开发"
,
@
"publishedTime"
: @
"2011-09-10"
};
//字典转模型,过滤name为nil的情况,把NSString转为NSDate
Book *book = [Book mj_objectWithKeyValues:dict];
//打印
NSLog(@
"name=%@, publishedTime=%@"
, book.name, book.publishedTime);</code>
的github地址点这里:CoderMJLee/MJExtensionMJExtension