1.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
| #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); }
|