iOS开发系列-Runtime运用场景

概述

Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的。
调用runtimeAPI需要导入都文件#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];
}

方法交换

程序在启动是会加载开发者编写的类,在方法的调用时动态的去查找方法的实现。如果实现方法交换的思路时在程序加载时交换方法的实现。

原文地址:https://www.cnblogs.com/CoderHong/p/9431763.html