当前位置: 首页 > news >正文

【iOS】JSONModel源码学习

JSONModel源码学习

  • 前言
  • JSONModel的使用
      • 最基础的使用
      • 转换属性名称
      • 自定义错误
      • 模型嵌套
      • JSONModel的继承
  • 源码实现
    • initWithDictionary
      • init
    • __doesDictionary
    • importDictionary
  • 优点

前言

之前了解过JSONModel的一些使用方法等,但是对于底层实现并不清楚了解,今天来学习一些JSONModel的源码流程,本篇博客进行一个记录。

JSONModel的使用

最基础的使用

先给出一个当我们单纯传入字典的时候,转化成模型类的用法:

@interface Person : JSONModel@property (nonatomic, copy)   NSString *name;
@property (nonatomic, copy)   NSString *sex;
@property (nonatomic, assign) NSInteger age;@end

使用字典来转换为模型:

NSDictionary *dict = @{@"name":@"Jack",@"age":@23,@"gender":@"male",};NSError *error;Person *person = [[Person alloc] initWithDictionary:dict error:&error];
NSLog(@"%@", person);

结果

<Person> [name]: Jack[age]: 23[gender]: male
</Person>

转换属性名称

有时传入的字典中的key发生了变化(比如说接口重构之类的原因)但是模型属性我们并不好改变,这个时候就需要有一个转化功能去修改:

这里举例将gender变为sex,那我们应该怎么操作呢

+ (JSONKeyMapper *)keyMapper {return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{@"gender":@"sex"}];
}

来看看结果:

在这里插入图片描述

自定义错误

我们可以自定义属于我们自己的错误判断,比如说我们要限制Person信息的age不能小于18,需要在模型的实现文件中:

- (BOOL)validate:(NSError *__autoreleasing *)error {if (![super validate: error])return NO;if (self.age < 18) {*error = [NSError errorWithDomain:@"Too young" code:10 userInfo:nil];NSError* errorLog = *error;NSLog(@"%@", errorLog.domain);return NO;}return YES;
}

结果:

在这里插入图片描述

会打印错误 信息,同时也不会转化模型

模型嵌套

当我给Person类加一个朋友列表的时候,这个时候就是Person类嵌套一个Friend类,然而这种情况应该怎么操作呢:

#import <Foundation/Foundation.h>
#import <JSONModel/JSONModel.h>
@protocol Friend
@end
NS_ASSUME_NONNULL_BEGIN@interface Friend : JSONModel@property (nonatomic, copy) NSString* name;
@property (nonatomic, assign) NSInteger age;@end@interface Person : JSONModel
@property (nonatomic, copy) NSString* name;
@property (nonatomic, copy) NSString* gender;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSArray<Friend> *friends;//数组,嵌套模型
@endNS_ASSUME_NONNULL_END

看看结果如何:

在这里插入图片描述

这就是几种JSONModel比较典型的使用,下面来看看底层源码的实现。

JSONModel的继承

笔者看源码发现JSONModel需要扫描父类直至JSONModel这个类,但是嵌套的实现使用的是协议,这里记录一下笔者AI到的继承的使用,若有不对还望指正。

JSONModel的继承是通常适用于返回的数据中都有一段公共字段,我们有很多类型数据的时候,他们都有id和created_at,那么我们可以创建一个BaseModel,令所有类型的model直接继承这个类型model,这样我们可以避免每个子类重写。

源码实现

先来一张流程图

在这里插入图片描述

这里大致讲解一下流程:

首先在这个模型类的对象被初始化的时候,遍历自身到所有的父类,获取所有的属性,将其保存到一个字典中去,获取传入字典的所有的key,将这些key同保存的属性进行匹配。若是匹配成功,就进行KVC赋值


JSONModel一共提供了四种初始化的方法,如下:

-(instancetype)initWithString:(NSString*)string error:(JSONModelError**)err;
-(instancetype)initWithString:(NSString *)string usingEncoding:(NSStringEncoding)encoding error:(JSONModelError**)err;
-(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;
-(instancetype)initWithData:(NSData *)data error:(NSError **)error;

这里我们从-(instancetype)initWithDictionary:(NSDictionary*)dict error:(NSError **)err;这种最经典的方法讲起:

initWithDictionary

先来看看这个方法的源码实现:

-(id)initWithDictionary:(NSDictionary*)dict error:(NSError**)err
{//检查参数是否为nilif (!dict) {if (err) *err = [JSONModelError errorInputIsNil];return nil;}//参数不是nil,但是也不是字典if (![dict isKindOfClass:[NSDictionary class]]) {if (err) *err = [JSONModelError errorInvalidDataWithMessage:@"Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'."];return nil;}//初始化self = [self init];if (!self) {//super init didn't succeedif (err) *err = [JSONModelError errorModelIsInvalid];return nil;}//检查用户定义的模型里的属性集合是否大于传入的字典里的key集合(如果大于,则返回NO)if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {return nil;}//字典的key与模型的属性的映射if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {return nil;}//可以重写[self validate:err]方法并返回No,令用户自定义错误去阻拦model的返回if (![self validate:err]) {return nil;}//model is valid! yay!return self;
}

总结

  • 1-4步中都是对错误的发现和处理
  • 方法5是真正的mapping
  • 方法6是作者自定义错误的方法,若是复合了自定义的错误,即使mapping成功也要返回nil
  • 方法7成功返回模型对象

在开始之前,先来了解一下JSONModel所持有的数据:

static const char * kMapperObjectKey;//自定义的mapper,具体使用方法在上面的例子
static const char * kClassPropertiesKey;//用来保存所有属性信息的NSDictionary
static const char * kClassRequiredPropertyNamesKey;//用来保存所有属性的名称NSSet
static const char * kIndexPropertyNameKey;

KeyMapper的使用

JSONModel 提供了一个叫做 JSONKeyMapper 的工具类,用于在 JSON 数据中查找与类属性名相对应的属性名,以便进行正确的映射。

JSONKeyMapper 是一个可定制的映射器,它提供了两种映射方式:

  • 下划线式(UnderscoreCase)映射:将下划线形式的 JSON 数据中的属性名转换成类属性名(如:foo_bar -> fooBar)。
  • 驼峰式(CamelCase)映射:将驼峰形式的 JSON 数据中的属性名转换成类属性名(如:fooBar -> foo_bar)。
JSONKeyMapper *mapper = [[JSONKeyMapper alloc] initWithModelToJSONDictionary:@{@"propertyOne": @"property_one",@"propertyTwo": @"property_two"
}];
MyModel *model = [[MyModel alloc] initWithDictionary:jsonDict error:nil];

init

我们从第三个方法init开始,看看流程:

-(id)init
{self = [super init];if (self) {//do initial class setup[self __setup__];}return self;
}-(void)__setup__
{//如果第一次实例化,就执行if (!objc_getAssociatedObject(self.class, &kClassPropertiesKey)) {[self __inspectProperties];}//若是存在自定义的mapper,就将其保存在关联对象中,key是KMapperObjectKeyid mapper = [[self class] keyMapper];if ( mapper && !objc_getAssociatedObject(self.class, &kMapperObjectKey) ) {objc_setAssociatedObject(self.class,&kMapperObjectKey,mapper,OBJC_ASSOCIATION_RETAIN // This is atomic);}
}

这里我们使用!objc_getAssociatedObject(self.class, &kMapperObjectKey) 的时候第一个参数使用self.class,这就是说这里我们获取当前类的关联对象,要是关联对象不存在,就说明当前类还没有被解析过,需要调用__inspectProperties方法进行解析

在第一次实例化的时候,所调用的__inspectProperties也是该框架的核心方法之一:其保存了所有需要赋值的属性,用作在将来与传进来字典进行映射

具体来说,该方法会使用运行时特性获取模型类的属性列表,并为每个属性创建一个 JSONModelProperty 对象,该对象包含属性名、数据类型、对应的 JSON 字段名等信息。然后,这些 JSONModelProperty 对象将存储在一个 NSMutableDictionary 对象中,以属性名作为键,JSONModelProperty 对象作为值。最后,该 NSMutableDictionary 对象将使用 objc_setAssociatedObject 方法与模型类关联起来,以便以后可以方便地访问。

-(void)__inspectProperties
{//    最终保存所有属性的字典,形式为:
//    {
//        age = "@property primitive age (Setters = [])";
//        friends = "@property NSArray* friends (Standard JSON type, Setters = [])";
//        gender = "@property NSString* gender (Standard JSON type, Setters = [])";
//        name = "@property NSString* name (Standard JSON type, Setters = [])";
//    }NSMutableDictionary* propertyIndex = [NSMutableDictionary dictionary];//获取当前的类名Class class = [self class];NSScanner* scanner = nil;NSString* propertyType = nil;// 循环条件:当class是JSONModel自己的时候会终止while (class != [JSONModel class]) {//JMLog(@"inspecting: %@", NSStringFromClass(class));//所有属性的个数unsigned int propertyCount;//获取属性列表objc_property_t *properties = class_copyPropertyList(class, &propertyCount);///遍历所有的属性for (unsigned int i = 0; i < propertyCount; i++) {//获得属性名称objc_property_t property = properties[i];//获得当前的属性const char *propertyName = property_getName(property);//name(C字符串)  //JSONModel里的每一个属性,都被封装成一个JSONModelClassProperty对象JSONModelClassProperty* p = [[JSONModelClassProperty alloc] init];p.name = @(propertyName);//propertyName:属性名称,例如:name,age,gender//获得属性类型const char *attrs = property_getAttributes(property);NSString* propertyAttributes = @(attrs);// T@\"NSString\",C,N,V_name// Tq,N,V_age// T@\"NSString\",C,N,V_gender// T@"NSArray",&,N,V_friends            NSArray* attributeItems = [propertyAttributes componentsSeparatedByString:@","];//说明是只读属性,不做任何操作if ([attributeItems containsObject:@"R"]) {continue; //to next property}//检查出是布尔值if ([propertyAttributes hasPrefix:@"Tc,"]) {p.structName = @"BOOL";//使其变为结构体}            //实例化一个scannerscanner = [NSScanner scannerWithString: propertyAttributes];[scanner scanUpToString:@"T" intoString: nil];[scanner scanString:@"T" intoString:nil];      if ([scanner scanString:@"@\"" intoString: &propertyType]) {                //属性是一个对象[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\"<"]intoString:&propertyType];//propertyType -> NSString                p.type = NSClassFromString(propertyType);// p.type = @"NSString"p.isMutable = ([propertyType rangeOfString:@"Mutable"].location != NSNotFound); //判断是否是可变的对象p.isStandardJSONType = [allowedJSONTypes containsObject:p.type];//是否是该框架兼容的类型//存在协议(数组,也就是嵌套模型)while ([scanner scanString:@"<" intoString:NULL]) {NSString* protocolName = nil;[scanner scanUpToString:@">" intoString: &protocolName];if ([protocolName isEqualToString:@"Optional"]) {p.isOptional = YES;} else if([protocolName isEqualToString:@"Index"]) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"p.isIndex = YES;
#pragma GCC diagnostic popobjc_setAssociatedObject(self.class,&kIndexPropertyNameKey,p.name,OBJC_ASSOCIATION_RETAIN // This is atomic);} else if([protocolName isEqualToString:@"Ignore"]) {p = nil;} else {p.protocol = protocolName;}//到最接近的>为止[scanner scanString:@">" intoString:NULL];}}            else if ([scanner scanString:@"{" intoString: &propertyType])                //属性是结构体[scanner scanCharactersFromSet:[NSCharacterSet alphanumericCharacterSet]intoString:&propertyType];p.isStandardJSONType = NO;p.structName = propertyType;}else {//属性是基本类型:Tq,N,V_age[scanner scanUpToCharactersFromSet:[NSCharacterSet characterSetWithCharactersInString:@","]intoString:&propertyType];//propertyType:qpropertyType = valueTransformer.primitivesNames[propertyType];              //propertyType:long//基本类型数组if (![allowedPrimitiveTypes containsObject:propertyType]) {//类型不支持@throw [NSException exceptionWithName:@"JSONModelProperty type not allowed"reason:[NSString stringWithFormat:@"Property type of %@.%@ is not supported by JSONModel.", self.class, p.name]userInfo:nil];}}NSString *nsPropertyName = @(propertyName);            //可选的if([[self class] propertyIsOptional:nsPropertyName]){p.isOptional = YES;}//可忽略的if([[self class] propertyIsIgnored:nsPropertyName]){p = nil;}//集合类Class customClass = [[self class] classForCollectionProperty:nsPropertyName];            if (customClass) {p.protocol = NSStringFromClass(customClass);}//忽略blockif ([propertyType isEqualToString:@"Block"]) {p = nil;}//如果字典里不存在,则添加到属性字典里(终于添加上去了。。。)if (p && ![propertyIndex objectForKey:p.name]) {[propertyIndex setValue:p forKey:p.name];}//setter 和 getterif (p){   //name ->NameNSString *name = [p.name stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[p.name substringToIndex:1].uppercaseString];// getterSEL getter = NSSelectorFromString([NSString stringWithFormat:@"JSONObjectFor%@", name]);if ([self respondsToSelector:getter])p.customGetter = getter;// settersp.customSetters = [NSMutableDictionary new];SEL genericSetter = NSSelectorFromString([NSString stringWithFormat:@"set%@WithJSONObject:", name]);if ([self respondsToSelector:genericSetter])p.customSetters[@"generic"] = [NSValue valueWithBytes:&genericSetter objCType:@encode(SEL)];for (Class type in allowedJSONTypes){NSString *class = NSStringFromClass([JSONValueTransformer classByResolvingClusterClasses:type]);if (p.customSetters[class])continue;SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set%@With%@:", name, class]);if ([self respondsToSelector:setter])p.customSetters[class] = [NSValue valueWithBytes:&setter objCType:@encode(SEL)];}}}free(properties);//再指向自己的父类,知道等于JSONModel才停止class = [class superclass];}//最后保存所有当前类,JSONModel的所有的父类的属性objc_setAssociatedObject(self.class,&kClassPropertiesKey,[propertyIndex copy],OBJC_ASSOCIATION_RETAIN);//使用关联对象与模型类关联起来
}

这里有几点需要注意:

  • 作者使用一个while函数,获取当前类和当前类的除去JSONModel的所有父类的属性保存在一个字典中。用来和传入的字典进行一个映射关系。
  • 作者使用JSONModelClassProperty类封装了JSONModel的每一个属性,这个类有两个重要的属性:一个是name,这是属性的名称;另一个是type,这是属性的类型
  • 作者将属性分为了以下几个类型:
    • 对象(不含有协议)
    • 对象(含有协议,即模型嵌套)
    • 基本数据类型
    • 结构体

__doesDictionary

-(BOOL)__doesDictionary:(NSDictionary*)dict matchModelWithKeyMapper:(JSONKeyMapper*)keyMapper error:(NSError**)err
{//拿到字典中所有的keyNSArray* incomingKeysArray = [dict allKeys];//返回保存所有属性名称的数组(name,age,gender...)NSMutableSet* requiredProperties = [self __requiredPropertyNames].mutableCopy;//从array中拿到setNSSet* incomingKeys = [NSSet setWithArray: incomingKeysArray];//若是用户自定义了mapper,进行一个转换if (keyMapper || globalKeyMapper) {NSMutableSet* transformedIncomingKeys = [NSMutableSet setWithCapacity: requiredProperties.count];NSString* transformedName = nil;//遍历需要转换的属性列表for (JSONModelClassProperty* property in [self __properties__]) {//被转换成的属性名称 gender(模型内) -> sex(字典内)transformedName = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;//拿到sex以后,查看传入的字典里是否有sex对应的值@try {value = [dict valueForKeyPath:transformedName];}@catch (NSException *exception) {value = dict[transformedName];}//若是值存在,就将sex添加到传入的keys数组中if (value) {[transformedIncomingKeys addObject: property.name];}}//用映射的键名称覆盖原始传入列表incomingKeys = transformedIncomingKeys;}//查看当前的model的属性的集合是否大于传入的属性集合,如果是,则返回错误。//也就是说模型类里的属性是不能多于传入字典里的key的,例如:if (![requiredProperties isSubsetOfSet:incomingKeys]) {//获取缺失属性的列表(获取多出来的属性)[requiredProperties minusSet:incomingKeys];//并非所有必需的属性都在 in - 输入无效JMLog(@"Incoming data was invalid [%@ initWithDictionary:]. Keys missing: %@", self.class, requiredProperties);if (err) *err = [JSONModelError errorInvalidDataWithMissingKeys:requiredProperties];return NO;}//not needed anymoreincomingKeys= nil;requiredProperties= nil;return YES;
}
  • 这个方法就将我们使用过程中重写的keyMapper方法中的属性名称进行了一个转换,按照我们的需求进行一个改变
  • 这里我们可以看到在最后一个if判断中说明,我们传入的字典数据中的key集合不能小于model类的定义的属性集合,也就是说我们model类中定义的属性集合必须要赋值。

importDictionary

终于到了将NSDictionary对象转化为JSONModel对象这一步了,来看看会发生什么:

-(BOOL)__importDictionary:(NSDictionary*)dict withKeyMapper:(JSONKeyMapper*)keyMapper validation:(BOOL)validation error:(NSError**)err
{//遍历保存的所有属性的字典for (JSONModelClassProperty* property in [self __properties__]) {//将属性的名称拿过来,作为key,用这个key来查找传进来的字典里对应的值NSString* jsonKeyPath = (keyMapper||globalKeyMapper) ? [self __mapString:property.name withKeyMapper:keyMapper] : property.name;//用来保存从字典里获取的值id jsonValue;        @try {jsonValue = [dict valueForKeyPath: jsonKeyPath];}@catch (NSException *exception) {jsonValue = dict[jsonKeyPath];}//字典不存在对应的keyif (isNull(jsonValue)) {//如果这个key是可以不存在的if (property.isOptional || !validation) continue;            //如果这个key是必须有的,则返回错误if (err) {NSString* msg = [NSString stringWithFormat:@"Value of required model key %@ is null", property.name];JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];*err = [dataErr errorByPrependingKeyPathComponent:property.name];}return NO;}        //获取 取到的值的类型Class jsonValueClass = [jsonValue class];BOOL isValueOfAllowedType = NO;//查看是否是本框架兼容的属性类型for (Class allowedType in allowedJSONTypes) {if ( [jsonValueClass isSubclassOfClass: allowedType] ) {isValueOfAllowedType = YES;break;}}        //如果不兼容,则返回NO,mapping失败if (isValueOfAllowedType==NO) {//type not allowedJMLog(@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass));if (err) {NSString* msg = [NSString stringWithFormat:@"Type %@ is not allowed in JSON.", NSStringFromClass(jsonValueClass)];JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];*err = [dataErr errorByPrependingKeyPathComponent:property.name];}return NO;}//如果是兼容的类型:if (property) {// 查看是否有自定义setter,并设置if ([self __customSetValue:jsonValue forProperty:property]) {continue;};// 基本类型if (property.type == nil && property.structName==nil) {//kvc赋值if (jsonValue != [self valueForKey:property.name]) {[self setValue:jsonValue forKey: property.name];}continue;}// 如果传来的值是空,即使当前的属性对应的值不是空,也要将空值赋给它if (isNull(jsonValue)) {if ([self valueForKey:property.name] != nil) {[self setValue:nil forKey: property.name];}continue;}// 1. 属性本身是否是jsonmodel类型if ([self __isJSONModelSubClass:property.type]) {//通过自身的转模型方法,获取对应的值JSONModelError* initErr = nil;id value = [[property.type alloc] initWithDictionary: jsonValue error:&initErr];if (!value) {               //如果该属性不是必须的,则略过if (property.isOptional || !validation) continue;//如果该属性是必须的,则返回错误if((err != nil) && (initErr != nil)){*err = [initErr errorByPrependingKeyPathComponent:property.name];}return NO;}            //当前的属性值为空,则赋值if (![value isEqual:[self valueForKey:property.name]]) {[self setValue:value forKey: property.name];}continue;} else {// 如果不是jsonmodel的类型,则可能是一些普通的类型:NSArray,NSString。。。// 是否是模型嵌套(带有协议)if (property.protocol) {//转化为数组,这个数组就是例子中的friends属性。jsonValue = [self __transform:jsonValue forProperty:property error:err];if (!jsonValue) {if ((err != nil) && (*err == nil)) {NSString* msg = [NSString stringWithFormat:@"Failed to transform value, but no error was set during transformation. (%@)", property];JSONModelError* dataErr = [JSONModelError errorInvalidDataWithMessage:msg];*err = [dataErr errorByPrependingKeyPathComponent:property.name];}return NO;}}// 对象类型if (property.isStandardJSONType && [jsonValue isKindOfClass: property.type]) {//可变类型if (property.isMutable) {jsonValue = [jsonValue mutableCopy];}//赋值if (![jsonValue isEqual:[self valueForKey:property.name]]) {[self setValue:jsonValue forKey: property.name];}continue;}// 当前的值的类型与对应的属性的类型不一样的时候,需要查看用户是否自定义了转换器(例如从NSSet到NSArray转换:- (NSSet *)NSSetFromNSArray:(NSArray *)array)if ((![jsonValue isKindOfClass:property.type] && !isNull(jsonValue))||//the property is mutableproperty.isMutable||//custom struct propertyproperty.structName) {Class sourceClass = [JSONValueTransformer classByResolvingClusterClasses:[jsonValue class]];//JMLog(@"to type: [%@] from type: [%@] transformer: [%@]", p.type, sourceClass, selectorName);NSString* selectorName = [NSString stringWithFormat:@"%@From%@:",(property.structName? property.structName : property.type), //target namesourceClass]; //source nameSEL selector = NSSelectorFromString(selectorName);//查看自定义的转换器是否存在BOOL foundCustomTransformer = NO;if ([valueTransformer respondsToSelector:selector]) {foundCustomTransformer = YES;                        } else {//try for hidden custom transformerselectorName = [NSString stringWithFormat:@"__%@",selectorName];selector = NSSelectorFromString(selectorName);if ([valueTransformer respondsToSelector:selector]) {foundCustomTransformer = YES;}}//如果存在自定义转换器,则进行转换if (foundCustomTransformer) {                        IMP imp = [valueTransformer methodForSelector:selector];id (*func)(id, SEL, id) = (void *)imp;jsonValue = func(valueTransformer, selector, jsonValue);if (![jsonValue isEqual:[self valueForKey:property.name]])[self setValue:jsonValue forKey:property.name];                        } else {                       //没有自定义转换器,返回错误NSString* msg = [NSString stringWithFormat:@"%@ type not supported for %@.%@", property.type, [self class], property.name];JSONModelError* dataErr = [JSONModelError errorInvalidDataWithTypeMismatch:msg];*err = [dataErr errorByPrependingKeyPathComponent:property.name];return NO;                        }} else {// 3.4) handle "all other" cases (if any)if (![jsonValue isEqual:[self valueForKey:property.name]])[self setValue:jsonValue forKey:property.name];}}}}return YES;
}

值得注意的是:

  • 作者在最后给属性赋值的时候使用的是kvc的setValue:ForKey:的方法。
  • 作者判断了模型里的属性的类型是否是JSONModel的子类,可见作者的考虑是非常周全的。
  • 整个框架看下来,有很多的地方涉及到了错误判断,作者将将错误类型单独抽出一个类(JSONModelError),里面支持的错误类型很多。

在设置属性值时,__importDictionary方法还会进行一些类型转换和校验

  • 如果属性是JSONModel类型,则将字典转换为该类型的JSONModel对象,并设置到当前属性中。如果属性是基本数据类型(如int、float等),则将字典中的数值转换为相应的数据类型,并设置到当前属性中。
  • 如果属性是NSDate类型,则将字典中的时间戳转换为NSDate对象,并设置到当前属性中。总之,__importDictionary方法的主要作用是将NSDictionary对象转换为JSONModel对象,并进行一些类型转换和校验。

这里有一点笔者之前了解不够的地方记录一下:

@property (nonatomic, strong) NSString<Optional> *optionalString; 

当我们使用<Optional>这个协议标记的时候,说明这个属性不是必须实现的,当我们没有实现这个属性的时候,其被赋值为nil

优点

  • Runtime动态解析model数据类型
  • keyMapper映射
  • KVC赋值
  • 使用关联对象来避免重复解析相同模型,很妙
http://www.lryc.cn/news/616222.html

相关文章:

  • 2025.8.10总结
  • mpv core_thread pipeline
  • 第16届蓝桥杯Scratch选拔赛初级及中级(STEMA)2025年4月13日真题
  • ARM保留的标准中断处理程序入口和外设中断处理程序入口介绍
  • Python设计模式 - 装饰模式
  • 双亲委派机制是什么?
  • 亚麻云之轻云直上EC2
  • 硬件开发_基于STM32单片机的智能电梯系统
  • 关键基础设施中的新兴技术如何扩大网络风险
  • Java .class文件反编译成 .java文件
  • LeetCode 括号生成
  • 机器学习数学基础:46.Mann-Kendall 序贯检验(Sequential MK Test)
  • AtomicStampedReference解决方案
  • QT常用控件三
  • 浏览器CEFSharp88+X86+win7 之js交互开启(五)
  • 深入理解C语言一维数组的本质:数组名、指针常量与访问细节
  • 女子试穿4条裤子留下血渍赔50元引争议:消费责任边界在哪?
  • 无须炮解,打开即是Pro版
  • (LeetCode 每日一题) 869. 重新排序得到 2 的幂 (哈希表+枚举)
  • Python中随机化列表元素的详细方法
  • LintCode第604题-滑动窗口内数的和
  • DAY36打卡
  • 自创论述类文本阅读:论温泉
  • ubuntu 安装内核模块驱动 DKMS 介绍
  • 基于Ubuntu20.04的环境,编译QT5.15.17源码
  • ubuntu22.04+samba
  • 正则表达式常用语法参考
  • 零基础学Java第三讲---运算符
  • CSS优先级、HTTP响应状态码
  • ​Microsoft Store 离线下载软件