概述
Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的。
调用runtime
API需要导入都文件#import <objc/runtime.h>
常用的runtime函数
// 获取一个类的Class类型(类对象)
objc_getClass(const char * _Nonnull name)
// 获取函数的IMP
class_getMethodImplementation(Class cls, SEL name)
// 获取对象某个方法
Method method = class_getInstanceMethod(Class cls, SEL name)
// 获取类的某个方法
Method method = class_getClassMethod(Class cls, SEL name);
// 交换方法
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
// 动态给类添加方法
class_addMethod(Class cls, SEL name, IMP imp, const char * types);
// 获取类的成员
class_copyIvarList(Class cls, unsigned int * outCount);
runtime使用场景
利用类扩展给对象添加属性
在类扩展中给类添加属性,默认只会生成属性的getter与setter方法声明,不会生成方法实现和带下划线成员变量
示例: 给UIView添加一个NSDictinonary和flag属性 ,flag为BooL类型,作为基本类型属性的代表
#import <UIKit/UIKit.h>
@interface UIView (Example)
// 动态添加基本数据类型成员变量
@property (nonatomic, assign) BOOL flag;
// 动态添加对象类型成员变量
@property (nonatomic, strong) NSDictionary *dict;
@end
#import "UIView+Example.h"
#import <objc/runtime.h>
@implementation UIView (Example)
/***************************动态添加基本数据类型成员变量***************************/
- (void)setFlag:(BOOL)flag
{
// 注意:参数二 需要传递一个key 这里建议用@selector(属性名),不用定义很多常量
objc_setAssociatedObject(self, @selector(flag), @(flag), OBJC_ASSOCIATION_ASSIGN);
}
- (BOOL)flag
{
id object = objc_getAssociatedObject(self, @selector(flag));
return object?[object boolValue]: NO;
}
/***************************动态添加对象类型成员变量***************************/
-(void)setDict:(NSDictionary *)dict
{
objc_setAssociatedObject(self, @selector(dict), dict, OBJC_ASSOCIATION_RETAIN);
}
- (NSDictionary *)dict
{
return objc_getAssociatedObject(self, @selector(dict));
}
@end
动态添加方法
class_addMethod(Class _Nullable cls, SEL name, IMP imp, const char * types);
第一个参数:类对象 可以通过objc_getClass
获取 第二个参数 动态添加的方法SEL
第三个参数 方法或者函数的实现地址 class_getMethodImplementation
方法实现
第四个参数:描述方法/函数的返回值类型、参数类型 使用@encode()
可获得类型的编码
下面示例给ViewController添加一个addNewMethod:
方法,然后通过performSelector
在程序运行时调用该方法。
// 动态添加的方法
- (void)addNewMethod:(UIButton *)button
{
NSLog(@"-------------------%@", button);
}
- (void)viewDidLoad {
[super viewDidLoad];
// 1.获取动态添加方法的地址IMP
IMP addNewMethodIMP = class_getMethodImplementation([self class], @selector(addNewMethod:));
// 2.获取方法的types
char types[100];
strcpy(types, @encode(void)); // 返回值类型
strcpy(types, @encode(id)); // 每个函数自带的id self参数
strcpy(types, @encode(SEL)); // 每个函数自带的SEL sel参数
strcpy(types, @encode(UIButton)); // 动态添加方法addNewMethod的button参数
// 3.动态绑定添加方法
class_addMethod(objc_getClass("ViewController"), @selector(addNewMethod:), addNewMethodIMP, types);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// 通过performSelector调用函数
[self performSelector:@selector(addNewMethod:) withObject:UIButton.new];
}
方法交换
程序在启动是会加载开发者编写的类,在方法的调用时动态的去查找方法的实现。如果实现方法交换的思路时在程序加载时交换方法的实现。