runtime基本原理与常用方法

本文详细介绍了Objective-C的runtime机制,包括其基本概念、结构体表示及如何通过runtime获取类的属性、方法、成员变量和协议列表等内容。此外,还探讨了如何利用runtime进行方法的动态添加与拦截。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 什么是runtime

runtime是基于C语言的一套API,C语言是基于面向过程的,而runtime在C的基础上进行了一次封装,使得C语言有了面向对象这一说。

换一种比较明了的说法:

我们平时写的OC代码其实都会在运行时转成C语言来运行,比如OC中调用方法会转化成C语言中的
id objc_msgSend ( id self, SEL op, … );
而oc中的对象在runtime中以结构体的形式来展示,结构体中包含着类名, 成员变量, 方法名,方法列表,协议列表等等。

一个类在runtime中的表示

struct objc_class {
    Class isa;//指针,顾名思义,表示是一个什么,
    //实例的isa指向类对象,类对象的isa指向元类

#if !__OBJC2__
    Class super_class;  //指向父类
    const char *name;  //类名
    long version;
    long info;
    long instance_size
    struct objc_ivar_list *ivars //成员变量列表
    struct objc_method_list **methodLists; //方法列表
    struct objc_cache *cache;//缓存
    //一种优化,调用过的方法存入缓存列表,下次调用先找缓存
    struct objc_protocol_list *protocols //协议列表
    #endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

有了这个结构体, 我们就可以通过遵循runtime的协议,来使用这个结构体。

例如 : 获取属性/方法/协议列表、拦截调用与动态添加等等(后续使用了会继续添加。。。)

a) 获取属性/方法/协议列表

最直接的一种用法,就是获取我们的结构体中存储的对象的属性、方法、协议等列表,从而获取其所有这些信息。
具体如下:

#import <objc/runtime.h>

// 输出类的一些信息
- (void)logInfo {
    unsigned int count;// 用于记录列表内的数量,进行循环输出

    // 获取属性列表
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (unsigned int i = 0; i < count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSLog(@"property --> %@", [NSString stringWithUTF8String:propertyName]);
    }

    // 获取方法列表
    Method *methodList = class_copyMethodList([self class], &count);
    for (unsigned int i; i < count; i++) {
        Method method = methodList[i];
        NSLog(@"method --> %@", NSStringFromSelector(method_getName(method)));
    }

    // 获取成员变量列表
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (unsigned int i; i < count; i++) {
        Ivar myIvar = ivarList[i];
        const char *ivarName = ivar_getName(myIvar);
        NSLog(@"Ivar --> %@", [NSString stringWithUTF8String:ivarName]);
    }

    // 获取协议列表
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
    for (unsigned int i; i < count; i++) {
        Protocol *myProtocal = protocolList[i];
        const char *protocolName = protocol_getName(myProtocal);
        NSLog(@"protocol --> %@", [NSString stringWithUTF8String:protocolName]);
    }
}

不过需要注意:
a )需要给类加几个属性、方法,遵循一些协议,否则是看不到输出信息的。
b )要使用这些获取的方法,需要导入头文件 #import

b) 拦截调用与动态添加
拦截调用:

就是在所有地方都找不到方法的实现的话,会出发拦截调用,可以自己去做一些处理避免程序崩溃。那在什么地方做处理呢?
NSObject有四个方法可以用来做处理:
// 调用不存在的类方法时触发,默认返回NO,可以加上自己的处理后返回YES
+ (BOOL)resolveClassMethod:(SEL)sel;

// 调用不存在的实例方法时触发,默认返回NO,可以加上自己的处理后返回YES
+ (BOOL)resolveInstanceMethod:(SEL)sel;

// 将调用的不存在的方法重定向到一个其他声明了这个方法的类里去,返回那个类的target
- (id)forwardingTargetForSelector:(SEL)aSelector;

// 将调用的不存在的方法打包成 NSInvocation 给你,自己处理后调用 invokeWithTarget: 方法让某个类来触发
- (void)forwardInvocation:(NSInvocation *)anInvocation;

例子:

@interface ViewController ()
@property (nonatomic, strong) UIButton *logBtn;

- (void)notHas;// 要调用的实例方法,没有具体实现
@end

- (void)viewDidLoad {
    [super viewDidLoad];

    // 添加按钮
    self.logBtn = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 20)];
    [self.logBtn setTitle:@"测 试" forState:UIControlStateNormal];
    [self.logBtn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
    // 添加没有实现的点击事件
    [self.logBtn addTarget:self action:@selector(notHas) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.logBtn];

}

// 拦截对不存在的方法的调用
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"notFind!");
    // 给本类动态添加一个方法
    if ([NSStringFromSelector(sel) isEqualToString:@"notHas"]) {
        class_addMethod(self, sel, (IMP)runAddMethod, "v@:*");
    }
    // 注意要返回YES
    return YES;
}

// 要动态添加的方法,这是一个C方法
 void runAddMethod(id self, SEL _cmd, NSString *string) {
     NSLog(@"动态添加一个方法来提示");
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值