美文网首页
无标题文章

无标题文章

作者: jsun789 | 来源:发表于2016-10-26 16:18 被阅读0次

Question

通常我们用Objective-C写的模型层遇到了什么问题?
在开发过程中,我们常常会从服务端获取数据,数据通常是JSON格式。 比较常见的做法是把JSON数据转为Model对象,这样我们可以从Model对象的属性读取数据。
如:
1、我们每次都要通过-initWithDictionarty:(NSDictionary *)dict解析json数据,把值一个一个赋值给MODEL对象,经常还会出现[[obj objectForKey:@“KEY”] objectAtIndex:index] objectForKey:@“”]这种层次比较深的遍历,层次关系也要小心处理,比较容易出错。
或者即使多了一层封装:

    _userAuthCode = authCode ? authCode : @"";
    _userAuthToken = token ? token : @"";
    _userUid = getStringInDict(jsonObject, @"uid");
    _pauth = getStringInDict(jsonObject, @"pauth");
    NSDictionary *info = getDictionaryInDict(jsonObject, @"info");
    _userUsername = getStringInDict(info, @"username");
    _userSex = getStringInDict(info, @"sex");

但是一样还要繁琐处理。
2、如果遇到服务端返回的类型不是确定的,更是蛋疼;有时候说是一个NSString,但是却给了个NSNumber,还有NSDate类型,给了NString类型,我们必须增加一些本来可以没有的判空。看到这么多if else就烦躁了。。

self.startKey = getStringInDict(result,@"startKey");
if (self.startKey == nil) {
  if (getIntInDict(result, @"startKey") == 0) {
   self.startKey = @"";
  }else{
   self.startKey = [NSString stringWithFormat:@"%d",getIntInDict(result,@"startKey")];
  }
}

3、还有就是接口出了问题,返回了NSNull,假设没有做好处理,还有可以就Crash.
4、还有Model—>Json
5、序列化归档问题

What:

mantleTitle
Mantle是一个用于简化Cocoa或Cocoa Touch程序中model层的第三方库。通常我们的应该中都会定义大量的model来表示各种数据结构,而这些model的初始化和编码解码都需要写大量的代码。

Mantle的优点在于能够大大地简化这些代码。是iOS和Mac平台下基于Objective-C编写的一个简单高效的模型层框架。
  Mantle源码中最主要的内容包括:

  • MTLModel类:通常是作为我们的Model的基类,该类提供了一些默认的行为来处理对象的初始化和归档操作,同时可以获取到对象所有属性的键值集合。
  • MTLJSONAdapter类:用于在MTLModel对象和JSON字典之间进行相互转换,相当于是一个适配器。
  • MTLJSONSerializing协议:需要与JSON字典进行相互转换的MTLModel的子类都需要实现该协议,以方便MTLJSONApadter对象进行转换。

##Use:
首先,看看Mantle的文件结构,文件还是蛮多的,不过常用的就那几个。MTDModel、MTLJSONAdapter

mantleTitle

TDModel

要使用的Model都必须继承MTDModel

初始化

MTLModel默认的初始化方法-init并没有做什么事情,只是调用了下[super init]。而同时,它提供了一个另一个初始化方法:

- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError **)error;

其中参数dictionaryValue是一个字典,它包含了用于初始化对象的key-value对。我们来看下它的具体实现:

- (instancetype)initWithDictionary:(NSDictionary *)dictionary error:(NSError **)error {
    ...
    for (NSString *key in dictionary) {
        // 1. 将value标记为__autoreleasing,这是因为在MTLValidateAndSetValue函数中,
        //    可以会返回一个新的对象存在在该变量中
        __autoreleasing id value = [dictionary objectForKey:key];
        // 2. value如果为NSNull.null,会在使用前将其转换为nil
        if ([value isEqual:NSNull.null]) value = nil;
        // 3. MTLValidateAndSetValue函数利用KVC机制来验证value的值对于key是否有效,
        //    如果无效,则使用使用默认值来设置key的值。
        //    这里同样使用了对象的KVC特性来将value值赋值给model对应于key的属性。
        //    有关MTLValidateAndSetValue的实现可参考源码,在此不做详细说明。
        BOOL success = MTLValidateAndSetValue(self, key, value, YES, error);
        if (!success) return nil;
    }
    ...
}

子类可以重写该方法,可以在设置完对象的属性后做进一步的处理或初始化工作,记住:应该通过super来调用父类的实现**。

游戏盒的例子

h:
#import <Mantle/Mantle.h>

@interface MTDCommonGameListModel : MTLModel<MTLJSONSerializing>
@property (nonatomic,strong) NSString*  gameId;
@property (nonatomic) int               star;
@property (nonatomic) BOOL              hasVideo;
@property (nonatomic) MTDGameDownloadUrlType downloadType;
@property (nonatomic,strong) NSDate*    date;
@property (nonatomic,strong) NSString*  imageUrl;
@property (nonatomic,strong) SubModel*  subModel;
…
m:
#import "MTDCommonGameListModel.h"

@implementation MTDCommonGameListModel

+ (NSDictionary *)JSONKeyPathsByPropertyKey{
    return @{
         @"gameId":@"id",
         @"star":@"star",
         @"hasVideo":@"hasVideo",
         @"downloadType":@"downloadType",
     @"date":@"date”,
         @"imageUrl":@"img",
         @"subModel":@"subModel"
    };
    
}
//服务端返回的类型兼容 gameId可支持服务端返回 字符串或数值类型
+ (NSValueTransformer *)gameIdJSONTransformer {
    return [MTLValueTransformer transformerUsingForwardBlock:^id(id value, BOOL *success, NSError *__autoreleasing *error) {
        return [value stringValue];
    }];
}
//枚举类型映射
+ (NSValueTransformer *)stateJSONTransformer {
    return [NSValueTransformer mtl_valueMappingTransformerWithDictionary:@{                                                @"Others": @(MTDGameDownloadUrlTypeOthers),                                                                      @"Itunes": @(MTDGameDownloadUrlTypeItunes),                                                                  @"ShortLink": @(MTDGameDownloadUrlTypeShortLink)
 }];
}
//date
+ (NSValueTransformer *)dateJSONTransformer { 
      return [MTLValueTransformer reversibleTransformerWithForwardBlock:^(NSNumber *dateNum) {
               return [NSDate dateWithTimeIntervalSince1970:dateNum.floatValue];  
 } 
 reverseBlock:^(NSDate *date) {
  return [NSString stringWithFormat:@"%f",[date timeIntervalSince1970]]; 
 }];
}
//Model里面的Model实现
+ (NSValueTransformer *)subModelJSONTransformer {
    return [MTLJSONAdapter dictionaryTransformerWithModelClass:SubModel.class];
}

- (instancetype)initWithDictionary:(NSDictionary *)dictionaryValue error:(NSError *__autoreleasing *)error{
    self = [super initWithDictionary:dictionaryValue error:error];
    if (self == nil) {
        return nil;
    }
    return self;
    
}

如果需要转换的属性可以增加对应的方法,通过运行时处理的,方法命名规则是 [属性JSONTransformer],对这个属性进行赋值时会调用这个方法先进行转换。
当JSON数据里有NSNull的类型时,我们不用做任何处理,会自动将该属性置为nil;

Model里面的Model实现

  • (NSValueTransformer *)subModelJSONTransformer {
    return [MTLJSONAdapter dictionaryTransformerWithModelClass:subModel.class];
    }

reverseBlock model —>JSON

reverseBlock是干什么的呢? 当要把Model转换回JSON数据时,如果设置了返回值,那么会将NSDate转回NSNumber返回JSON数据。
我们可以调用 MTLJSONAdapter的

+ (NSDictionary *)JSONDictionaryFromModel:<#(id<MTLJSONSerializing>)#> error:<#(NSError *__autoreleasing *)#>;

方法将一个Model实例转回JSON数据。

合并对象

- (void)mergeValueForKey:(NSString *)key fromModel:(MTLModel *)model;

该方法将当前对象指定的key属性的值与model参数对应的属性值按照指定的规则来进行合并,这种规则由我们自定义的-mergeFromModel:方法来确定。如果我们的子类中实现了-mergeFromModel:方法,则会调用它;如果没有找到,且model不为nil,则会用model的属性的值来替代当前对象的属性的值。具体实现如下:

- (void)mergeValueForKey:(NSString *)key fromModel:(MTLModel *)model {
    NSParameterAssert(key != nil);
    // 1. 根据传入的key拼接"mergeFromModel:"字符串,并从该字符串中获取到对应的selector
    //    如果当前对象没有实现-mergeFromModel:方法,且model不为nil,则用model的属性值
    //    替代当前对象的属性值
    //
    //    MTLSelectorWithCapitalizedKeyPattern函数以C语言的方式来拼接方法字符串,具体实现请
    //    参数源码,在此不详细说明
    SEL selector = MTLSelectorWithCapitalizedKeyPattern("merge", key, "FromModel:");
    if (![self respondsToSelector:selector]) {
        if (model != nil) {
            [self setValue:[model valueForKey:key] forKey:key];
        }
        return;
    }
    // 2. 通过NSInvocation方式来调用对应的-mergeFromModel:方法。
     NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:  [self methodSignatureForSelector:selector]];
     invocation.target = self;
     invocation.selector = selector;
     [invocation setArgument:&model atIndex:2];
     [invocation invoke];
 }

此外,MTLModel还提供了另一个方法来合并两个对象所有的属性值,即:

 - (void)mergeValuesForKeysFromModel:(MTLModel *)model;

需要注意的是model必须是当前对象所属类或其子类。

<NSCoding>, <NSCopying>, -isEqual:和-hash

在你的子类里面生命属性,MTLModel可以提供这些方法的默认实现

Mantle配合归档

MTLModel默认实现了 NSCoding协议,可以利用NSKeyedArchiver方便的对对象进行归档和解档。

Mantle配合Core Data

….这个后续有时间再补充,目前还没整理

##****参考:
https://github.com/mantle/mantle
https://segmentfault.com/a/1190000003882034
http://www.jianshu.com/p/937266eb6635

相关文章

  • 无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标

    无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章 无标题文章无标题文章无标题文章无...

  • 无标题文章

    无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章

  • 无标题文章

    无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标...

  • 无标题文章

    无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标...

  • fasfsdfdf

    无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标...

  • 无标题文章

    无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标...

  • 无标题文章

    无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标...

  • 无标题文章

    无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标...

  • 无标题文章

    无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章无标题文章

  • 无标题文章

    无标题文章 无标题文章 无标题文章无标题文章 无标题文章 无标题文章

网友评论

      本文标题:无标题文章

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