iOS runtime方法调用与消息转发

导语: iOS runtime为开发者提供了很多灵活便捷的方法,使得在运行时也可以改变类的结构。这篇文章主要是从方法调用作为切入点,来学习&记录runtime的理论知识。

一、方法调用

在OC中,运行时贯穿了整个工程的运行过程,每一个方法的调用都离不开运行时的工作。

在讨论OC时,我们经常说 向对象“发消息” 而不是“调用”,原因就在于在整个程序运行过程中,每一次实际上所调用的方法并不是已经完全绑定好的,编译器会把OC方法的调用,转换成objc_msgsend函数,这个函数会动态的寻找下一步要执行的方法。也正是这个函数,完成了动态绑定的整个过程。

objc_msgsend的大致运行流程如下图,其中需要重点关注的是“寻找方法实现”这一步。

寻找方法实现可以概括为以下几步:

二、消息转发

当OC找不到代码中调用的方法时,在crash之前我们还有机会通过重写以下NSObject的四个方法来进行处理:

//当调用一个不存在的类方法时调用
+ (BOOL)resolveClassMethod:(SEL)sel;

//当调用一个不存在的实例方法时调用
+ (BOOL)resolveInstanceMethod:(SEL)sel;

//将这个不存在的方法重定向到其它类处理,需要返回一个实例
- (id)forwardingTargetForSelector:(SEL)aSelector;

//将这个不存在的方法打包成NSInvocation丢进来。需要调用invokeWithTarget:给某个能够执行方法的实例
- (void)forwardInvocation:(NSInvocation *)anInvocation;


整个流程如下图所示:

·首先调用resolveInstanceMethod(以调用实例方法来举例)

·如果返回NO,那么调用forwardingTargetForSelector

·如果返回nil,那么调用forwardInvocation

我们现在来动手动态的添加一个方法。首先,在viewdidload里调用一个不存在的方法:

TestClass *tstObj = [[TestClass alloc]init];
[tstObj performSelector:@selector(lalaLand)];

再在TestClass.m中添加以下代码:

void addMethod(id self, SEL _cmd){
    NSLog(@"addMethod complete.");
}

+ (BOOL)resolveInstanceMethod:(SEL)sel{
    //给本类动态添加一个方法
    class_addMethod(self, sel, (IMP)addMethod, "v@:*");
    NSLog(@"resolveInstanceMethod return.");
    return YES;
}

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    NSLog(@"forwardingTargetForSelector complete.");
    return nil;
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
    NSLog(@"forwardInvocation complete.");
}

打印log:

2017-02-28 16:26:12.992 NormalTryTry[12628:28082853] resolveInstanceMethod return.
2017-02-28 16:26:25.348 NormalTryTry[12628:28082853] addMethod complete.

上面的代码实际上只运行到了resolveInstanceMethod就成功返回,因为在这个方法中我们已经给到了系统一个方法实现,并返回了TES,这时候系统就不会再向两个forward抛出消息了。

简单说来,这四个方法都是用来添加未处理方法的。区别在于,resolveInstanceMethod是在本类中添加方法,并告诉系统该方法是否执行;forwardingTargetForSelector 是自己处理不了,转给其它实例做处理;如果经过以上几步还是处理不了,那么就走到了forwardInvocation中,系统会把这个方法的所有信息都打包给我们,做最后的处理。

消息转发有很多灵活的应用,对于crash防崩溃、lua-wax都是很重要的技术点~

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏TechBox

一份走心的iOS开发规范前言约定(一)命名规范(二)编码规范2.14 内存管理规范本文参考文章其他有价值的文章

3098
来自专栏iOS技术

iOS多线程应用(一):概述、NSThread

现在网络上有着大量的关于多线程的文章,“深入剖析”、“底层原理”这些看似高大上的字眼很多,然而大部分文章完全没达到那个地步,仅仅是使用API的水平,关键是还有很...

3026
来自专栏iOS技术杂谈

深入源码理解YYCache 、SDWebImage、AFNetworking、NSCache 缓存方式与对比

深入源码理解YYCache 、SDWebImage、AFNetworking、NSCache 缓存方式与对比 转载请注明出处 https://cloud.ten...

4877
来自专栏菩提树下的杨过

java学习:Hibernate学习-用oracle sequence序列生成ID的配置示例

接上回继续,TMP_EMP中的ID是根据序列SQ_TMP_EMP来生成的,需要在TmpEmp.hbm.xml中设置:   <id name="id" type=...

1809
来自专栏哈雷彗星撞地球

Runtime系列(二)--Runtime的使用场景

Runtime 理解介绍的文章非常多,我只想讲讲Runtime 可以用在哪里,而我在项目里哪些地方用到了runtime。多以实际使用过程为主,来介绍runtim...

963
来自专栏進无尽的文章

编码篇-数组的相关使用

数据的常规方法的使用本文不做描述,本文旨在归纳一些数组不是很常用的方法使用。算作一个归纳笔记,后续会持续更新.....

772
来自专栏程序员Gank

iOS-代码规范

利用上周的业余时间把这篇规范整理了出来,我会将这篇规范作为我们iOS团队的代码规范,并且还会根据读者的反馈,项目的实践和研究的深入做不定时更新,还希望各位朋友看...

1502
来自专栏程序员维他命

iOS 代码规范

花了一个月的时间结合几篇博客和书籍写了这套 iOS 代码规范(具体参考底部的参考文献部分)。这套代码规范除了有仅适用于 iOS 开发的部分,还有其他的比较通用性...

912
来自专栏『不羁阁』行走的少年专栏

iOS多线程:『pthread、NSThread』详尽总结

1384
来自专栏程序员维他命

《Effective Objective-C》干货三部曲(二):规范篇

该三部曲系列是由笔者将《Effective Objective-C 》这本书的52个知识点分为三大类进行了归类整理而成:

631

扫码关注云+社区