iOS_Runtime

iOS Runtime理解与运用

2017-05-12  本文已影响90人  大大盆子

ping怎么这么高?


哈哈,进入正题!

什么是Runtime?

这还要说?run( 运行)、time(时),runtime(运行时),没毛病!好了,我们都知道Objective-C是基于C衍生出来的动态语言,加入了面向对象特征和消息机制,这都归功于Runtime,它将静态语言在编译和链接时期做的事放到了运行时来处理。在我们Objective-C中,runtime是一个运行时库,是一套纯C的API。

typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY; //结构体指针,指向类对象,这样,当我们向对象发送消息时,runtime库会根据这个isa指针找到对象所属的类,然后从类的方法列表及父类方法列表中查找消息对应的selector指向的函数实现,然后执行。
};
/// A pointer to an instance of a class.
typedef struct objc_object *id; //该类型对象可以转换成任意对象

当然类也是对象,由Class类型表示,它实际上是一个指向 objc_class结构体的指针,可以到objc/runtime.h中查看定义

typedef struct objc_class *Class;
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;  //结构体的指针,每个对象都有一个isa指针,实例的isa指向类对象,类对象的isa指向元类。

    #if !__OBJC2__
    Class super_class                                        //父类
    const char *name                                         //类名
    long version                                             //类的版本信息,默认为0
    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
}

上面引出一个元类(Meta Class):类对象的类,它储存着一个类所有的类方法,每个类都有一个单独的meta-class,因为每个类的类方法基本不可能完全相同,那么细想,元类也是有isa指针的,它指向谁呢?为了不让这种结构无限延伸下去,isa指向基类的meta-class,而基类的meta-class的isa指针指向它自己。

  1. 检查消息对象是否为nil,如果是,则什么都不做。
  2. 通过receiver 的isa指针找到receiver对应的类,从类的方法缓存中通过SEL查找IMP,有,调用;没有,往下。(类的方法很多,如果每次都去方法列表中查找就会影响到效率,所以每一个类都会有一个方法缓存)。
  3. 从方法列表中查找,有,调用;没有,往下。
  4. 查找父类的方法缓存,有,直接调用;没有,往下。
  5. 查找父类的方法列表,有,直接调用;没有,往下,一直找到基类,以上就是一个正常的消息发送过程。
  6. 如果在基类也没有找到,则会调用NSObject的决议方法 + (BOOL)resolveInstanceMethod:(SEL)sel+ (BOOL)resolveClassMethod:(SEL)sel,返回YES则重启一次消息的发送过程,返回NO则会进入消息转发
  7. 调用- (id)forwardingTargetForSelector:(SEL)aSelector,如果实现了这个方法,并返回一个非nil的对象,则这个对象会作为消息的新接收者,且消息会被分发到这个对象,当然这个对象不能是self自身,否则就是出现无限循环;如果返回的是nil,往下继续。
  8. 调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector ,生成一个方法签名,接着会创建一个NSInvocation(消息调用对象,包含target,selector,以及方法签名),并传给- (void)forwardInvocation:(NSInvocation *)anInvocation,进行转发调用。

消息异常处理

当消息异常的时候,会执行方法决议以及消息转发,在上面的消息发送过程中也具体介绍了,这里借用一张图片来更好的理解


再看调用结果,效果是一样的,只是不同的处理方式而已,从打印上可以看出,这是在- (id)zm_forwardingTargetForSelector:(SEL)aSelector中进行处理的,也是替换的- (id)forwardingTargetForSelector:(SEL)aSelector方法,找到返回的备用对象去执行调用的方法。

调用结果也是一样的,这也是异常消息处理最后的机会,错过了就没机会了。


小结

这里主要是通过一个异常的消息来演示消息发送以及转发的过程,并在消息转发过程中对异常消息的捕捉及处理,我把这些写到NSObject的类目中主要为了防止开发中调用了不存在的方法导致的crash,当然如果在子类中重写了这些方法,可以调用super,也是一样的。


基础用法

打印结果如我们所料,能够拿到所有的方法,也能调用私有方法。



总结

这里主要是写了自己对Runtime的理解,以及在平时开发中的运用。Runtime里面的API有很多,目前对它的理解以及运用程度有限,所以借此来抛砖引玉,同时有什么错误的地方,希望朋友们能够指出改正,谢谢。

上一篇下一篇

猜你喜欢

热点阅读