Mantle
阅读一个库的源码,首先要知道,我们为什么需要这一类的库。
Mantle的目的
Mantle 的诞生是为了更方便的将服务端返回的数据映射为我们的 Model。
简单来说,我们在写 app 的时候,经常需要把服务端返回的数据和我们自己创建 model 关联起来,这样,在和 View 层交互的时候就可以使用 model 而不是直接使用字典。
那么,我们如果不使用 Mantle 的情况下。是如何创建一个 Model 并且把服务端返回的数据填充到这个Model里呢?我们来看看 Mantle 给的例子,一般是这样的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
typedef enum : NSUInteger { GHIssueStateOpen, GHIssueStateClosed } GHIssueState; @interface GHIssue : NSObject @property (nonatomic, copy, readonly) NSURL *URL; @property (nonatomic, copy, readonly) NSURL *HTMLURL; @property (nonatomic, copy, readonly) NSNumber *number; @property (nonatomic, assign, readonly) GHIssueState state; @property (nonatomic, copy, readonly) NSString *reporterLogin; @property (nonatomic, copy, readonly) NSDate *updatedAt; @property (nonatomic, strong, readonly) GHUser *assignee; @property (nonatomic, copy, readonly) NSDate *retrievedAt; @property (nonatomic, copy) NSString *title; @property (nonatomic, copy) NSString *body; - (id)initWithDictionary:(NSDictionary *)dictionary; @end |
然后 .m 文件里的实现一般是这样的。
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 |
@implementation GHIssue + (NSDateFormatter *)dateFormatter { NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'"; return dateFormatter; } - (id)initWithDictionary:(NSDictionary *)dictionary { self = [self init]; if (self == nil) return nil; _URL = [NSURL URLWithString:dictionary[@"url"]]; _HTMLURL = [NSURL URLWithString:dictionary[@"html_url"]]; _number = dictionary[@"number"]; if ([dictionary[@"state"] isEqualToString:@"open"]) { _state = GHIssueStateOpen; } else if ([dictionary[@"state"] isEqualToString:@"closed"]) { _state = GHIssueStateClosed; } _title = [dictionary[@"title"] copy]; _retrievedAt = [NSDate date]; _body = [dictionary[@"body"] copy]; _reporterLogin = [dictionary[@"user"][@"login"] copy]; _assignee = [[GHUser alloc] initWithDictionary:dictionary[@"assignee"]]; _updatedAt = [self.class.dateFormatter dateFromString:dictionary[@"updated_at"]]; return self; } |
想象一下,如果你的 Model 有几十种,相当于你要写几十次这样重复的代码。因此,为了减少这种重复性的工作,应运而生了 Mantle 这样的库。
Mantle的功能
了解了Mantle为何诞生。那么,我们就要看看Mantle到底为我们解决了什么样的问题?
- 避免了写重复性的 init 方法,通过服务端返回的数据自动生成 model ,也可以利用model来反序列化生成 JSON 。
- 当model里的某个属性名称和服务端的某个字段的名字不一样的时候,可以利用
+ (NSDictionary *)JSONKeyPathsByPropertyKey
这个方法来进行字段的匹配。 - 可以通过
+JSONTransformer
这个命名方法来声明一个转换方法。举例来说就是,一般服务端返回日期的时候是以时间戳的方式返回的,通常是一个长整形的数字,1464116217 ,但是你声明的对应的property是一个NSDate
类型,这时候,你就需 ȑ们自己创建 model 关联起来,这样,在和 View 层交互的时候就可以使用 model 而不是直接使用字典。
那么,我们如果不使用 Mantle 的情况下。是如何创建一个 Model 并且把服务端返回的数据填充到这个Model里呢?我们来看看 Mantle 给的例子,一般是这样的。12345678910111213141516171819202122typedef enum : NSUInteger {GHIssueStateOpen,GHIssueStateClosed} GHIssueState;@interface GHIssue : NSObject@property (nonatomic, copy, readonly) NSURL *URL;@property (nonatomic, copy, readonly) NSURL *HTMLURL;@property (nonatomic, copy, readonly) NSNumber *number;@property (nonatomic, assign, readonly) GHIssueState state;@property (nonatomic, copy, readonly) NSString *reporterLogin;@property (nonatomic, copy, readonly) NSDate *updatedAt;@property (nonatomic, strong, readonly) GHUser *assignee;@property (nonatomic, copy, readonly) NSDate *retrievedAt;@property (nonatomic, copy) NSString *title;@property (nonatomic, copy) NSString *body;- (id)initWithDictionary:(NSDictionary *)dictionary;@end然后 .m 文件里的实现一般是这样的。
123456789101112131415161718192021222324252627282930313233@implementation GHIssue+ (NSDateFormatter *)dateFormatter {NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];dateFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'";return dateFormatter;}- (id)initWithDictionary:(NSDictionary *)dictionary {self = [self init];if (self == nil) return nil;_URL = [NSURL URLWithString:dictionary[@"url"]];_HTMLURL = [NSURL URLWithString:dictionary[@"html_url"]];_number = dictionary[@"number"];if ([dictionary[@"state"] isEqualToString:@"open"]) {_state = GHIssueStateOpen;} else if ([dictionary[@"state"] isEqualToString:@"closed"]) {_state = GHIssueStateClosed;}_title = [dictionary[@"title"] copy];_retrievedAt = [NSDate date];_body = [dictionary[@"body"] copy];_reporterLogin = [dictionary[@"user"][@"login"] copy];_assignee = [[GHUser alloc] initWithDictionary:dictionary[@"assignee"]];_updatedAt = [self.class.dateFormatter dateFromString:dictionary[@"updated_at"]];return self;}想象一下,如果你的 Model 有几十种,相当于你要写几十次这样重复的代码。因此,为了减少这种重复性的工作,应运而生了 Mantle 这样的库。
Mantle的功能
了解了Mantle为何诞生。那么,我们就要看看Mantle到底为我们解决了什么样的问题?
- 避免了写重复性的 init 方法,通过服务端返回的数据自动生成 model ,也可以利用model来反序列化生成 JSON 。
- 当model里的某个属性名称和服务端的某个字段的名字不一样的时候,可以利用
+ (NSDictionary *)JSONKeyPathsByPropertyKey
这个方法来进行字段的匹配。 - 可以通过
+JSONTransformer
这个命名方法来声明一个转换方法。举例来说就是,一般服务端返回日期的时候是以时间戳的方式返回的,通常是一个长整形的数字,1464116217 ,但是你声明的对应的property是一个NSDate
类型,这时候,你就需 Ɓ进行长整形 -> 日期类型的转换,所以 Mantle 提供了这种方式,来进行方便的转换。 - 自动的decode和encoding。方便 Model 的归档。
当然,Mantle 远不止这些功能。但是,我们先搞清楚主要的功能,那么一些次要的功能自然就迎刃而解了。
Mantle如何实现这些功能?
如何实现自动将Dictionary的值自动的赋给Model里对应的property?
我们先来尝试一下自己实现自动化的赋值。
熟悉OC的朋友应该知道NSObject有一个方法,叫做- (void)setValuesForKeysWithDictionary:(NSDictionary *)keyedValues;
这个方法就是利用kvc,直接让 model 调用- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
方法,通过遍历传入的字典的key,对model进行赋值。
举个简单例子,现在我们有个Model叫做User。它的property和init方法如下。