【iOS】YYModel
前言
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类。
objc
#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里经常会出现数组或集合,例如我们天气预报中网络请求得到的数据:
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
方法。
objc
#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
objc
#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类的对象即可。
objc
#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
json
{
"author":{
"name":"J.K.Rowling",
"birthday":"1965-07-31T00:00:00+0000"
},
"book":"Harry Potter",
"page":256
}
黑名单与白名单
- 黑名单:指定哪些属性不参与JSON转换。
objc
+(NSArray<NSString *> *)modelPropertyBlacklist {
return @[@"location"];
}
这部分代码加在我们上面的demo中,location将不参与JSON转换,编译结果就会改变:
- 白名单:指定只有哪些属性参与JSON转换,其余的都会被忽略。
objc
+(NSArray<NSString *> *)modelPropertyWhitelist {
return @[@"code"];
}
白名单使得只有code能参与JSON转换,编译结果能实现同样的效果:

总结
这样,我们就可以更方便地将网络请求中得到的数据转化成我们的OC对象,方便对数据操作处理。