iOS高级理论:Runtime应用

news/2024/7/20 21:12:56 标签: ios

一、遍历类的属性,快速归档

在 iOS 中,可以使用 Runtime 遍历类的属性来实现快速的归档(Archiving)操作。归档是将对象转换为数据流以便存储或传输的过程。下面是一个简单的示例,展示如何使用 Runtime 遍历类的属性进行归档操作:

假设有一个名为 Person 的类,我们想要对其属性进行归档操作:

#import <objc/runtime.h>

@interface Person : NSObject <NSCoding>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end

@implementation Person

- (void)encodeWithCoder:(NSCoder *)coder {
    unsigned int count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);
    
    for (int i = 0; i < count; i++) {
        objc_property_t property = properties[i];
        NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
        id propertyValue = [self valueForKey:propertyName];
        
        [coder encodeObject:propertyValue forKey:propertyName];
    }
    
    free(properties);
}

- (instancetype)initWithCoder:(NSCoder *)coder {
    self = [super init];
    if (self) {
        unsigned int count;
        objc_property_t *properties = class_copyPropertyList([self class], &count);
        
        for (int i = 0; i < count; i++) {
            objc_property_t property = properties[i];
            NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
            id propertyValue = [coder decodeObjectForKey:propertyName];
            
            [self setValue:propertyValue forKey:propertyName];
        }
        
        free(properties);
    }
    return self;
}

@end

在上面的示例中,encodeWithCoder: 方法遍历了 Person 类的所有属性,并将属性的值使用 NSCoder 进行归桋操作。initWithCoder: 方法则对归档的数据进行解档,恢复对象的状态。

通过使用 Runtime 遍历类的属性,我们可以实现一个通用的归档和解档方法,而无需手动编写大量的归档代码。这样可以提高代码的复用性和可维护性。

二、字典转模型

1、创建一个NSObject的分类

@interface NSObject (Json)
+ (instancetype)dictToModel:(NSDictionary *)dict;
@end

2、实现分类中字典转模型的方法

#import "NSObject+Json.h"
#import <objc/runtime.h>

@implementation NSObject (Json)

+ (instancetype)dictToModel:(NSDictionary *)dict
{
    id obj = [[self alloc] init];
    unsigned int count = 0;
    
    Ivar *ivars = class_copyIvarList([self class], &count);
    
    for (int i=0; i<count; i++) {
        Ivar ivar = ivars[i];
        NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];
        [name deleteCharactersInRange:NSMakeRange(0, 1)];        
        [obj setValue:dict[name] forKey:name];
    }
    
    return obj;
}
@end

3、调用字典转模型的方法

- (void)viewDidLoad {
    [super viewDidLoad];

    NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
    [dict setObject:@"张三" forKey:@"name"];
    [dict setObject:@"20" forKey:@"age"];
    [dict setObject:@"北京" forKey:@"address"];
    
    Student *student = [Student dictToModel:dict];
    
    NSLog(@"name:%@\n",student.name);
    NSLog(@"age:%@\n",student.age);
    NSLog(@"address:%@\n",student.address);
}

4、运行结果

2019-04-13 10:51:32.136568+0800 AppLife[19195:4640916] name:张三
2019-04-13 10:51:32.136707+0800 AppLife[19195:4640916] age:20
2019-04-13 10:51:32.136803+0800 AppLife[19195:4640916] address:北京

三 防止数组插入空值

1、创建一个NSMutableArray的分类

@interface NSMutableArray (Extension)

@end

2、实现分类中方法的交换

#import "NSMutableArray+Extension.h"
#import <objc/runtime.h>

@implementation NSMutableArray (Extension)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class cls = NSClassFromString(@"__NSArrayM");
        Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
        Method method2 = class_getInstanceMethod(cls, @selector(cs_insertObject:atIndex:));
        method_exchangeImplementations(method1, method2);
    });
}

- (void)cs_insertObject:(id)anObject atIndex:(NSUInteger)index {
    if (anObject == nil) {
        return;
    }
    
    [self cs_insertObject:anObject atIndex:index];
}

@end

3、调用

#import "NSMutableArray+Extension.h"
#import <objc/runtime.h>

@implementation NSMutableArray (Extension)

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class cls = NSClassFromString(@"__NSArrayM");
        Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
        Method method2 = class_getInstanceMethod(cls, @selector(cs_insertObject:atIndex:));
        method_exchangeImplementations(method1, method2);
    });
}

- (void)cs_insertObject:(id)anObject atIndex:(NSUInteger)index {
    if (anObject == nil) {
        return;
    }
    
    [self cs_insertObject:anObject atIndex:index];
}

@end

4、运行结果

2019-04-13 11:24:19.562363+0800 AppLife[20661:4661256] (
    Test
)

运用Rutime中交换方法的思想,还可以实现拦截所有按钮的点击时间和防止字典中插入空值等。

四、给分类添加属性

1、在分类里声明一个属性

#import "Student.h"

@interface Student (Test)
@property (nonatomic, copy) NSString *englishName;
@end

2、实现get和set方法

@implementation Student (Test)

- (void)setEnglishName:(NSString *)englishName
{
	// 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略
    objc_setAssociatedObject(self, @"EnglishName", englishName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (NSString *)englishName
{
    return objc_getAssociatedObject(self, @"EnglishName");
}

@end

五、其他

(1) 实现第一个场景:跟踪程序每个ViewController展示给用户的次数,可以通过Method Swizzling替换ViewDidAppear初始方法。创建一个UIViewController的分类,重写自定义的ViewDidAppear方法,并在其+load方法中实现ViewDidAppear方法的交换。

(2) 开发中常需要在不改变某个类的前提下为其添加一个新的属性,尤其是为系统的类添加新的属性,这个时候就可以利用Runtime的关联对象(Associated Objects)来为分类添加新的属性了。

(3) 实现字典的模型和自动转换,优秀的JSON转模型第三方库JSONModel、YYModel等都利用runtime对属性进行获取,赋值等操作,要比KVC进行模型转换更加强大,更有效率。阅读YYModel的源码可以看出,YY大神对NSObject的内容进行了又一次封装,添加了许多描述内容。其中YYClassInfo是对Class进行了再次封装,而YYClassIvarInfo、YYClassMethodInfo、YYClPropertyInfo分别是对Class的Ivar、Method和property进行了封装和描述。在提取Class的相关信息时都运用了Runtime。


http://www.niftyadmin.cn/n/5395240.html

相关文章

echarts在线样式

makeapie echarts社区图表可视化案例makeapie echarts图表可视化案例, 分享你的可视化作品https://www.makeapie.cn/echarts

SQL注入漏洞解析--less-46

我们先看一下46关 他说让我们先输入一个数字作为sort,那我们就先输入数字看一下 当我们分别输入1&#xff0c;2&#xff0c;3可以看到按照字母顺序进行了排序&#xff0c;所以它便是一个使用了order by语句进行排序的查询的一种查询输出方式 当输入时出现报错提示&#xff0c;说…

基于相位的运动放大:如何检测和放大难以察觉的运动(01/2)

基于相位的运动放大&#xff1a;如何检测和放大难以察觉的运动 目录 一、说明二、结果的峰值三、金字塔背景3.1 可操纵金字塔3.2 亚倍频程复数可控金字塔 四、基本方针4.1 1D 问题陈述4.2 一维方法4.3 实际实施说明 五、放大倍率的限制5.1 空间支持的影响5.2 频带的影响 六、推…

创建shel脚本 -- 自动备份用户文件

文章目录 1 按天自动备份&#xff08;归档&#xff09;文件1.1 准备工作1.2 编写配置文件和归档脚本1.2.1 编写配置文件1.2.2 编写归档脚本 1.3 运行脚本1.4 运行分析1.5 添加到cron表中1.5.1 编辑cron表1.5.2 测试脚本是否可用 2 按小时自动备份&#xff08;归档&#xff09;文…

linux之前后端项目部署与发布

目录 前言 简介 一、安装Nginx 二、后端部署 2.1多个tomcat负载均衡 2.2 负载均衡 2.3 后端项目部署 三、前端部署 1.解压前端 2.Nginx配置文件修改 3.IP域名映射 4.重启Nginx服务 前言 上篇博主已经讲解过了单机项目的部署linux之JAVA环境配置JDK&Tomcat&a…

Excel的中高级用法

单元格格式&#xff0c;根据数值的正负分配不同的颜色和↑ ↓ 根据数值正负分配颜色 2-7 [蓝色]#,##0;[红色]-#,##0 分配颜色的基础上&#xff0c;根据正负加↑和↓ 2↑-7↓ 其实就是在上面颜色的代码基础上加个 向上的符号↑&#xff0c;或向下的符号↓ [蓝色]#,##0↑;[红色…

Nginx的反向代理:实现灵活的请求转发和内容缓存

一、引言&#xff1a;代理服务器的简介 本节介绍代理服务器的基本配置。学习如何通过不同协议将 NGINX 请求传递给代理的服务器&#xff0c;修改发送到代理服务器的客户端请求标头&#xff0c;以及配置来自代理服务器的响应缓冲。 代理通常用于在多个服务器之间分配负载&…

【Spring连载】使用Spring Data的Repositories----从聚合根(Aggregate Roots)发布事件

【Spring连载】使用Spring Data的Repositories----从聚合根Aggregate Roots发布事件 由存储库管理的实体是聚合根。在域驱动设计应用程序中&#xff0c;这些聚合根通常发布域事件。Spring Data提供了一个名为DomainEvents的注解&#xff0c;你可以在聚合根的方法上使用该注解&a…