OC学习39.2:Runtime实际应用

张建 lol

1.runtime交换方法

场景:当第三方框架或者系统原生的方法不满足需求的时候,可以在不改动原有方法的基础上,添加额外的功能。

方式:利用 OCruntime 机制

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#import "UIImage+A.h"
#import "objc/runtime.h"

@implementation UIImage (A)
/*
作用:把类加载进内存的时候调用,只会调用一次
调用:方法应先交换,再去调用
*/
+ (void)load{
// 1.获取image方法地址
Method originMethod = class_getClassMethod(self, @selector(imageNamed:));
// 2.获取zj_imageNamed方法地址
Method newMethod = class_getClassMethod(self, @selector(zj_imageNamed:));
// 3.交换方法地址
method_exchangeImplementations(originMethod, newMethod);
}
// 加载图片 且 带判断是否加载成功
+ (UIImage *)zj_imageNamed:(NSString *)name{
UIImage * img = [UIImage zj_imageNamed:name];
if (img) {
NSLog(@"runtime交互方法 -> 图片加载成功");
}else {
NSLog(@"runtime交互方法 -> 图片加载失败");
}
return img;
}
@end

2.Runtime给分类添加属性

场景:给系统的类添加额外属性的时候

原理:利用 OC 的 runtime机制。

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@interface Person (A)
@property (nonatomic,copy)NSString * name;
@property (nonatomic,assign)int age;
@end

#import "Person+A.h"
#import "objc/runtime.h"

@implementation Person (A)
// 1.String类型
// 我们需要在.m里声明这个key
static void * name_key = &name_key;
- (void)setName:(NSString *)name{
objc_setAssociatedObject(self, name_key, name, OBJC_ASSOCIATION_COPY);
}
- (NSString *)name{
return objc_getAssociatedObject(self, name_key);
}

// 2.Int型
// 我们需要在.m里声明这个key
static void * age_key = &age_key;
- (void)setAge:(int)age{
objc_setAssociatedObject(self, age_key, @(age), OBJC_ASSOCIATION_ASSIGN);
}
- (int)age{
NSNumber * number = objc_getAssociatedObject(self, age_key);
return [number intValue];
}
@end

3.动态添加方法

场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。

原理:利用 OC 的 runtime机制,使用 performSelector 添加方法,相当于懒加载机制

代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#import "ZJPerson.h"
#import "objc/runtime.h"

@implementation ZJPerson
// 当一个方法没有实现,但是又调用了,就会调用调用下面的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(eat)){
/**
class 给哪个类添加方法
SEL 方法编码
IMP 方法实现,就是一个函数指针
type 方法类型,苹果官方文档可以查看
*/
class_addMethod([ZJPerson class], sel, (IMP)eat, "v@:");
}else if (sel == @selector(play:)){
class_addMethod([ZJPerson class], sel, (IMP)play, "v@:@");
}
return [super resolveInstanceMethod:sel];
}
// 定义函数
/*
一个方法默认都有两个参数:self _cmd(隐士参数)
self:方法调用者
_cmd:调用方法的编号
*/
void eat(id self, SEL _cmd) {
NSLog(@"调用了%@的%@方法",self,NSStringFromSelector(_cmd));
}
void play(id self, SEL _cmd, id objc) {
NSLog(@"调用了%@的%@方法 Play%@",self,NSStringFromSelector(_cmd),objc);
}
@end

4.Runtime字典转模型

思路:利用运行时,遍历模型中所有属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值(从提醒:字典中取值,不一定要全部取出来);提供一个NSObject分类,专门字典转模型,以后所有模型都可以通过这个分类实现字典转模型。

考虑情况:
1.当字典的key和模型的属性匹配不上。
2.模型中嵌套模型(模型属性是另外一个模型对象)。
3.数组中装着模型(模型的属性是一个数组,数组中是一个个模型对象)。

5.实现NSCoding的归档接档

原理描述:用runtime提供的函数遍历Model自身所有属性,并对属性进行encode和decode操作。
核心方法:在Model的基类中重写方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
unsigned int outCount;
Ivar * ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i ++) {
Ivar ivar = ivars[i];
NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
}
free(ivars);
}
return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int outCount;
Ivar * ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i ++) {
Ivar ivar = ivars[i];
NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
free(ivars);
}
  • Post title:OC学习39.2:Runtime实际应用
  • Post author:张建
  • Create time:2020-08-18 15:23:24
  • Post link:https://redefine.ohevan.com/2020/08/18/OC/OC学习39.2:Runtime实际应用/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.