runtime原理分析

runtime是oc中一个比较底层的纯c语言库,包含了很多底层c语言的api

runtime其实和我们编程密切相关,平时编写的oc代码中,在程序运行时,其实最终都是转成runtime的c语言代码


可以这么理解,oc的底层实际上是在调用一个个c函数,如何找到这些c函数的工作,就是由runtime去负责了


要理解runtime,首先要理解类的结构


类的本质是什么? 实际上就是一个类似结构体的玩意,那么类里面有什么内容呢,以下一张图就能够很清晰的理解


一个Person类的内部构造

isa就是“is a”,对于所有继承了NSObject的类其对象也都有一个isa指针。这个isa指针指向关于这个对象所属的类的定义。​

任何直接或间接继承了NSObject的类,它的实例对象(instacne objec)中都有一个isa指针,指向它的类对象(class object)。这个类对象(class object)中存储了关于这个实例对象(instace object)所属的类的定义的一切:包括变量,方法,遵守的协议等等

以下这图可以很清楚的表述上面的关系

Class isa and superclass relationship from Google

简单来说,一个类的对象的isa指针会指向这个类,这个类也有一个isa指针会指向这个类的元祖类(meta class)。

一个类以及其元祖类(meta class) 都会有一个super class 的指针指向其父类,一直到根类(NSObject),NSObject的元祖类的super class 会指回到NSObject类,至于NSObject是没有super class 的,其super class 指针会指向nil

至于类和元祖类是什么关系呢? 首先,他们都是对象,根本上都是objc_class对象,因此也有类对象和元祖类对象的一说。区别在于,类对象中包含了这个类的实例变量,实例方法的定义,而元祖类对象中包含类的方法的定义。


搞清楚对象和类的这层关系之后,要理解起来就轻松多了,你可以理解成isa 和 super class 就是一个门牌号或者路标,runtime就是根据这些路标去找到相应的静态方法和变量的。

-------------------------------------- 华丽的分割线   ----------------------------------

搞清楚指针之后,之后就看重点了

ivars(成员变量列表)

        这个属性 objc_ivar_list 结构体类型,其实就是一个链表,存储多个objc_ivar,而objc_ivar又是一个结构体,用来存储类的单个成员变量的信息

         简单粗暴一点理解,ivar 就是成员变量

objc_ivar里面又有什么内容呢?见下图


objc_ivar结构


methodlists(方法列表)

      同ivars一样理解,实际上这两个结构体几乎是一样的,也是一个objc_method_list 的结构体, 也是一个链表,存储多个objc_method,  而objc_method 又是一个结构体,用来存储类的某个方法信息

我们也可以来一张图来展示objc_method的风采


objc_method

看到SEL估计都不陌生了,一个方法的SEL 其实是一个C的字符串,并且会在OC的runtime中注册这个selector,这个操作一般是在加载到内存的时候就完成了这一步注册操作,即在+ load 的方法中

实际上我们平时调用方法,都不是一步到位,实际上是通过selector找到该方法,然后再找到这个方法的实现(IMP),IMP本质上就是一个函数指针,指向c函数

举个栗子方便理解:

[ a  sayHello]  ---> 在a对象中通过其isa指针找到其类对象--->找到methodList ---> 根据"sayHello"这个selector找到对应的方法 ---> 找到其对应的IMP指针  --->  c函数

以上仅仅是方便理解而已,因为实际上,上面调用方法的环节还要加入缓存池的操作(cache)


Cache(方法缓存池)

          二话不说,先上图


objc_cache

           Cache其实就是一个存储Method的链表,主要是为了优化方法调用的性能。当调用一个方法的时候,会先去缓存池找,如果没找到,再去methodLists查找


------------------------------------------又是华丽的分割线-----------------------------


消息发送

 当调用一个方法时[a sayHello],其实是被编译器转化为

objc_msgSend ( id self, SEL op, ... )

1.根据a实例对象的isa找到其对应的类对象

2.优先在类对象中的cache查找sayHello方法,如果找不到,再到methodLists查找

3.如果找不到,通过super_class 指针向父类查找,如果找不到,找父类的父类,一直到NSObject

4.一旦找到了,则执行IMP的实现


容错机制

要是找不到怎么办?一开始我也以为会抛出异常,崩溃,实际上在崩溃之前还有三次容错机制进行处理,按照以下顺序:

     Method Resolution

     Fast Forwarding

     Normal Forwarding

上图最直接:


容错处理的最后三步


Method Resolution

首先Objective-C在运行时调用+ resolveInstanceMethod:或+ resolveClassMethod:方法,让你添加方法的实现。如果你添加方法并返回YES,那系统在运行时就会重新启动一次消息发送的过程。

举一个简单例子,定义一个类Message,它主要定义一个方法sendMessage,下面就是它的设计与实现:


@interface Message : NSObject

- (void)sendMessage:(NSString *)word;

@end


@implementation Message

- (void)sendMessage:(NSString *)word

{

NSLog(@"normal way : send message = %@", word);

}

@end

如果我在viewDidLoad方法中创建Message对象并调用sendMessage方法:


- (void)viewDidLoad {

[super viewDidLoad];

Message *message = [Messagenew];

[message sendMessage:@"Sam Lau"];

}

控制台会打印以下信息:

normal way : send message = Sam Lau

但现在我将原来sendMessage方法实现给注释掉,覆盖resolveInstanceMethod方法:


#pragma mark - Method Resolution

/// override resolveInstanceMethod or resolveClassMethod for changing sendMessage method implementation

+ (BOOL)resolveInstanceMethod:(SEL)sel

{

    if(sel == @selector(sendMessage:)) {

        class_addMethod([selfclass], sel, imp_implementationWithBlock(^(id self, NSString *word) {

        NSLog(@"method resolution way : send message = %@", word);

        }),"v@*");

    }

returnYES;

}

控制台就会打印以下信息:

method resolution way : send message = Sam Lau

注意到上面代码有这样一个字符串"v@*,它表示方法的参数和返回值,详情请参考Type Encodings。

如果resolveInstanceMethod方法返回NO,运行时就跳转到下一步:消息转发(Message Forwarding)。

Fast Forwarding

如果目标对象实现- forwardingTargetForSelector:方法,系统就会在运行时调用这个方法,只要这个方法返回的不是nil或self,也会重启消息发送的过程,把这消息转发给其他对象来处理。否则,就会继续Normal Fowarding。

继续上面Message类的例子,将sendMessage和resolveInstanceMethod方法注释掉,然后添加forwardingTargetForSelector方法的实现:


#pragma mark - Fast Forwarding

- (id)forwardingTargetForSelector:(SEL)aSelector

{

    if(aSelector == @selector(sendMessage:)) {

    return[MessageForwardingnew];

    }

    return nil;

}

此时还缺一个转发消息的类MessageForwarding,这个类的设计与实现如下:


@interface MessageForwarding : NSObject

- (void)sendMessage:(NSString *)word;

@end


@implementation MessageForwarding

- (void)sendMessage:(NSString *)word

{

NSLog(@"fast forwarding way : send message = %@", word);

}

@end

此时,控制台会打印以下信息:

fast forwarding way : send message = Sam Lau

这里叫Fast,是因为这一步不会创建NSInvocation对象,但Normal Forwarding会创建它,所以相对于更快点。

Normal Forwarding

如果没有使用Fast Forwarding来消息转发,最后只有使用Normal Forwarding来进行消息转发。它首先调用methodSignatureForSelector:方法来获取函数的参数和返回值,如果返回为nil,程序会Crash掉,并抛出unrecognized selector sent to instance异常信息。如果返回一个函数签名,系统就会创建一个NSInvocation对象并调用-forwardInvocation:方法。

继续前面的例子,将forwardingTargetForSelector方法注释掉,添加methodSignatureForSelector和forwardInvocation方法的实现:


#pragma mark - Normal Forwarding

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

{

    NSMethodSignature *methodSignature = [super methodSignatureForSelector:aSelector];

    if(!methodSignature) {

        methodSignature = [NSMethodSignature signatureWithObjCTypes:"v@:*"];

    }

    return methodSignature;

}

- (void)forwardInvocation:(NSInvocation *)anInvocation

{

    MessageForwarding *messageForwarding = [MessageForwardingnew];

    if([messageForwarding respondsToSelector:anInvocation.selector]) {

        [anInvocation invokeWithTarget:messageForwarding];

    }

}


-----------------------------------华丽的分割线-------------------------------------------


结束语: runtime是个好东西,虽然本人平时日常开发很少用到,但是了解其原理以及底层结构,会让你对整个oc的运作有一个更好的理解


最后,本文章也是参考刘耀柱大神的文章写出来的,更多关于runtime的干货,在下面链接:


http://www.csdn.net/article/2015-07-06/2825133-objective-c-runtime/6

你可能感兴趣的:(runtime原理分析)