【iOS】YYModel
【iOS】YYModel
- 前言
- JSONModel和YYModel
- YYModel
- 优点
- 使用方法
- Model与JSON互转
- 容器类属性
- Model的嵌套
- 黑名单与白名单
- 总结
前言
YYModel是YYKit的高效组件之一,在项目中使用MVC架构时,可以简化数据处理。这里学习后可以对天气预报的仿写进行一个优化。
JSONModel和YYModel
- JSONModel:一个用来把JSON数据和OC模型类自动映射的开源框架。JSONModel将json数据直接映射到对象中,用于简化JSON数据与数据模型之间的转换,对于获取模型嵌套数据更加方便。
- YYModel:一个高性能、轻量级的JSON模型框架。YYModel将JSON与OC对象高效地相互转换,支持复杂模型、容器类、属性映射等,适用于对性能要求高的移动应用。
总的来说,二者作用类似,但YYModel性能上相比JSONModel更加高效。
YYModel
优点
- 高性能:转换效率接近手写代码。
- 自动类型转换:对象类型能自动转换。
- 类型安全:在转换过程中所有的类型都会被验证,以确保类型安全。
- 非侵入性:不需要让模型类继承自基类。
- 轻量级:整个库只包含5个文件。
- 文档和测试覆盖:100%文档覆盖,99.6代码覆盖。
使用方法
Model与JSON互转
JSON一般是服务器返回的数据,格式是键值对({ "name":"Tom", "age":18 }
),我们在iOS开发中,需要将其转换为我们自己在OC里定义的类来承载这些数据。
这里需要注意的是:当JSON中的对象类型与Model属性不一致时,YYModel将会进行自动转换。其中自动转换不支持的值将会被忽略,以避免各种潜在的崩溃问题。
使用一个简单demo实现一下:
首先别忘了保证工程里有YYModel库,最简单的方法是用CocoaPods导入第三方库:
如果导入运行时遇到这样的报错:
可能原因是旧版本的 Xcode 在编译 ARC 时依赖一个 libarclite_*.a
辅助库。而新版本的 SDK(iOS 17、18 之后)已经不再自带这些文件,但编译工具链里有些配置/脚本还在找这个库,就导致出现报错。
这里只做一个了解:
libarclite
:Apple早期编译ARC代码时的一个运行时支持库,用来
- 给ARC提供一些 runtime 辅助函数
- 解决一些弱引用场景下的底层问题
- 早期(iOS 5 ~ iOS 11)必须依赖它完成ARC的编译和运行
这里我们只需要把我们的 Podfile 文件修改为为 iOS 12.0 起即可:
我们通过调用yy_modelWithJSON
和yy_modelToJSONObject
两个方法就可以实现JSON数据转化为model数据、model数据转化为NSObject类。
#import <Foundation/Foundation.h>@interface User : NSObject@property(nonatomic, assign) UInt64 uidNum;//C语言基本整数类型,无符号的64位整数
@property(nonatomic, copy) NSString *name;
@property(nonatomic, strong) NSDate *createdTime;@end#import "ViewController.h"
#import "User.h"
#import "YYModel.h"@interface ViewController ()@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];NSDictionary *json = @{@"uidNum":@123, @"name":@"Cherry", @"createdTime":@"2025-09-17T15:44:00+0000"};User *user = [User yy_modelWithJSON:json];NSLog(@"name:%@, uidNum:%llu, createdTime:%@", user.name, user.uidNum, user.createdTime);NSDictionary *jsonBack = [user yy_modelToJSONObject];NSLog(@"json:%@", jsonBack);
}@end
编译结果:
容器类属性
在JSON里经常会出现数组或集合,例如我们天气预报中网络请求得到的数据:
{code = 200;location = ({adm1 = "\U9655\U897f\U7701";adm2 = "\U897f\U5b89";country = "\U4e2d\U56fd";fxLink = "https://www.qweather.com/weather/xi'an-101110101.html";id = 101110101;isDst = 0;lat = "34.34321";lon = "108.93965";name = "\U897f\U5b89";rank = 11;type = city;tz = "Asia/Shanghai";utcOffset = "+08:00";});refer = {license = ("QWeather Developers License");sources = (QWeather);};
}
我们这里的location就是一个数组,而这个数组里包含了Model。遇到这种JSON里出现容器类属性的情况,我们需要调用+(NSDictionary<NSString *,**id**> *)modelContainerPropertyGenericClass
方法。
#import <Foundation/Foundation.h>
#import "LocationModel.h"
#import "YYModel.h"
#import "AFNetworking.h"@interface Model : NSObject<YYModel>@property(nonatomic, strong) NSString *code;
@property(nonatomic, copy) NSArray *location;@end#import <Foundation/Foundation.h>
#import "AFNetworking.h"
#import "Model.h"@interface LocationModel : NSObject@property(nonatomic, strong) NSString *adm1;
@property(nonatomic, strong) NSString *adm2;
@property(nonatomic, strong) NSString *country;
@property(nonatomic, strong) NSString *fxLink;
@property(nonatomic, strong) NSString *id;
@property(nonatomic, strong) NSString *isDst;
@property(nonatomic, strong) NSString *lat;
@property(nonatomic, strong) NSString *lon;
@property(nonatomic, strong) NSString *name;
@property(nonatomic, strong) NSString *rank;
@property(nonatomic, strong) NSString *type;
@property(nonatomic, strong) NSString *tz;
@property(nonatomic, strong) NSString *utcOffset;-(void)dataLoad;@end
#import "LocationModel.h"@implementation LocationModel-(void)dataLoad {NSString *cityName = @"西安";NSString *city = [cityName stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];NSString *urlString = [NSString stringWithFormat:@"https://geoapi.qweather.com/v2/city/lookup?location=%@&key=3344bfece6c74eaa911a5f857c30df82", city];[[AFHTTPSessionManager manager] GET:urlString parameters:nil headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {//NSLog(@"%@", responseObject);Model *model = [Model yy_modelWithJSON:responseObject];NSDictionary *json = [model yy_modelToJSONObject];NSLog(@"%@", json);} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {NSLog(@"error");}];
}@end#import "Model.h"@implementation Model+(NSDictionary<NSString *,id> *)modelContainerPropertyGenericClass {return @{@"location":[LocationModel class]};
}@end
整个过程的逻辑是,网络请求后,先读取第一层属性(code,location等),当读取到类似于location容器类属性时,再调用+(NSDictionary<NSString *,id> *)modelContainerPropertyGenericClass
方法告诉返回Location这个类对象,也就是我们在这个类中写的NSString类型属性,这样我们就会得到输出:
Model的嵌套
让一个Model的一个属性是另一个Model类的对象即可。
#import <Foundation/Foundation.h>@interface Author : NSObject@property(nonatomic, strong) NSString *name;
@property(nonatomic, strong) NSString *birthday;@end#import <Foundation/Foundation.h>
#import "Author.h"@interface Book : NSObject@property(nonatomic, strong) NSString *book;
@property(nonatomic, assign) NSUInteger page;
@property(nonatomic, strong) Author *author;@end
{"author":{"name":"J.K.Rowling","birthday":"1965-07-31T00:00:00+0000"},"book":"Harry Potter","page":256
}
黑名单与白名单
- 黑名单:指定哪些属性不参与JSON转换。
+(NSArray<NSString *> *)modelPropertyBlacklist {return @[@"location"];
}
这部分代码加在我们上面的demo中,location将不参与JSON转换,编译结果就会改变:
- 白名单:指定只有哪些属性参与JSON转换,其余的都会被忽略。
+(NSArray<NSString *> *)modelPropertyWhitelist {return @[@"code"];
}
白名单使得只有code能参与JSON转换,编译结果能实现同样的效果:
总结
这样,我们就可以更方便地将网络请求中得到的数据转化成我们的OC对象,方便对数据操作处理。