【iOS】YYModel

【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_modelWithJSONyy_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对象,方便对数据操作处理。

相关推荐
00后程序员张2 小时前
iOS 26 兼容测试实战,机型兼容、SwiftUI 兼容性改动
android·ios·小程序·uni-app·swiftui·cocoa·iphone
wenxiaocsdn3 小时前
macos制作可以启动的iso引导文件
macos
望获linux13 小时前
【Linux基础知识系列:第一百四十篇】理解SELinux与系统安全
linux·运维·服务器·数据库·chrome·macos
奇舞精选15 小时前
CEF框架实践:构建Mac混合桌面应用
macos·浏览器
2501_9275393021 小时前
Permute 媒体文件格式转换【音视频图像文件转换】(Mac电脑)
macos·音视频·媒体·格式转换
布拉格沃兹基硕德1 天前
MacOS报错“zsh: command not found: brew”【已解决】
macos·bug
阿里嘎多哈基米1 天前
Mac系统,Docker的MySQL + 本地 Canal
mysql·macos·docker·canal
xo198820111 天前
Xcode 26 could not locate developer disk image for this device 无法定位开发者磁盘镜像
ide·macos·xcode