赞
踩
在 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。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。