iOS知识加固(一)-Runtime篇

我只是知识的搬运工,为了应对面试和巩固知识体系,简化和整理了有关runtime的知识点,以求能快速理解和记忆。

(一)Runtime概念

  1. Runtime是用C、C++、汇编写的一套框架
  2. Objective-C语言是以Runtime为核心对C的扩展
  3. Runtime使得Objective-C拥有面向对象和动态的特性
  4. Runtime的核心就是消息机制(Messaging)

Runtime形成的动态特性:

  1. 数据类型推迟到运行时确定
  2. 方法推迟到运行时进行选择和变更

以我个人的理解,程序是到运行时将我们所写的OC语言转换成C语言,进而转换成计算机识别的汇编语言,而由OCC转换时,Runtime库根据我们的代码联系上下文形成明确的指令。

消息机制秉承万物皆对象的思想,将类、实例对象、方法等当做一个消息指令:哪个对象,执行了哪个方法,附带了哪些参数,主谓宾集齐便凑成一句完整的话传递给计算机。

1.1 消息传递

OC中类、对象以及方法本质都是结构体,这个结构体就是一个信息集合的单位,OC底层实现中涉及以下几个概念:

  • 类对象(objc_class)
  • 实例(objc_object)
  • 元类(Meta Class)
  • Method(objc_method)
  • SEL(objc_selector)
  • IMP
  • 类缓存(objc_cache)
  • Category(objc_category)

1.1.1 isa指针

所有的实例对象其实都是同样的结构体(objc_object),id也是这个结构体类型,故能指向所有对象,而这个objc_object结构体内只有一个指针isa,指向这个对象的类对象(objc_class),类对象的结构体里包含属性列表(ivars)、方法列表(methodLists)等,类对象的isa指针指向类对象的类,称之为元类(metaclass),另外类对象在编译期间产生了实例对象,是单例,所以isa表达了类和实例的逻辑关系(参见下图,详细底层代码请阅读结尾博文)

  • 对象(instance)的isa指向类(class object)
  • 类(class object)的isa指向元类(metaclass)
  • 元类(metaclass)的isa指向根元类(root metaclass)
isa指针指向

1.1.2 method

Method是方法的结构体,其中包含SELIMP和方法类型属性,个人理解SEL是方法索引,IMP是方法的具体实现。

1.1.3 类缓存(objc_cache)和Category(objc_category)

一个class 往往只有 20% 的函数会被经常调用,可能占总调用次数的 80% 。每个消息都需要遍历一次objc_method_list 并不合理。如果把经常被调用的函数缓存下来,那可以大大提高函数查询的效率。这也就是objc_class 中另一个重要成员objc_cache做的事情。
Category是表示一个指向分类的结构体的指针,其结构体内包含实例方法集合(instanceMethods)、类方法集合(classMethods)、协议集合(protocols)、属性集合(instanceProperties)、要拓展的类(cls)和类名称。

1.2 消息转发

消息传递过程中如果没有找到方法,系统给出挽回的三次机会。

1.2.1 动态方法解析

首先,Objective-C运行时会调用+(BOOL)resolveInstanceMethod:(SEL)sel或者+ (BOOL)resolveClassMethod:(SEL)sel,让你有机会提供一个函数实现。如果你添加了函数并返回YES, 那运行时系统就会重新启动一次消息发送的过程。询问是否有动态添加方法来进行处理。

1.2.2 备用接收者

如果目标对象实现了-(id)forwardingTargetForSelector:(SEL)aSelector,Runtime 这时就会调用这个方法,给你把这个消息转发给其他对象的机会。

1.2.3 完整消息转发(消息重定向)

如果在上一步还不能处理未知消息,则唯一能做的就是启用完整的消息转发机制了。
首先它会发送-methodSignatureForSelector:消息获得函数的参数和返回值类型。如果-methodSignatureForSelector:返回nilRuntime则会发出-doesNotRecognizeSelector:消息,程序这时也就挂掉了。如果返回了一个函数签名,Runtime就会创建一个NSInvocation对象并发送-forwardInvocation:消息给目标对象。

(二)API

  • objc_msgSend(对象, @selector(方法),参数)
  • objc_getClass("类名", sel):返回当前类,当你找的类不存在,返回nil;
  • objc_lookUpClass("类名", sel):返回当前类,当你找的类不存在,返回nil;
  • objc_getRequiredClass("类名", sel):返回当前类,当你要找的类不存在的话会闪退,慎用。
  • object_getClass("类名", sel):通过对象返回当前类
  • class_addMethod([Person class], sel, (IMP)eat, "v@:@"):1.类类型;2.方法编号;3.方法实现,函数指针;4.返回值类型(c语言字符串),看文档
  • Ivar *ivars = class_copyIvarList([Person class], &count)
  • const char *name = ivar_getName(ivar)
  • free(ivars)
  • method_exchangeImplementations(Method, Method)

(三)应用

  • 关联对象(Objective-C Associated Objects)给分类增加属性(objc_setAssociatedObject)
  • 方法魔法(Method Swizzling)方法添加和替换和KVO实现
  • 消息转发(热更新)解决Bug(JSPatch)
  • 实现NSCoding的自动归档和自动解档
  • 实现字典和模型的自动转换(MJExtension)

练习

  1. isa指针指向问题
    BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
    BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
    BOOL res3 = [(id)[Person class] isKindOfClass:[NSObject class]];
    BOOL res4 = [(id)[Person class] isMemberOfClass:[NSObject class]];
    BOOL res5 = [(id)[Person class] isKindOfClass:[Person class]];
    BOOL res6 = [(id)[Person class] isMemberOfClass:[Person class]];
    NSLog(@"\n%d\n%d\n%d\n%d\n%d\n%d",res1,res2,res3,res4,res5,res6);
  1. 利用Runtime,在程序运行的过程中,动态创建一个类;
  2. 利用Runtime,在程序运行的过程中,动态创建一个类的属性/方法;
  3. 遍历一个类的所有成员变量;

声明:参考以下博文,经过自己修改加工,为记录学习使用
iOS Runtime详解
iOS Runtime《一》 类Class 相关
你真的明白isKindOfClass 和 isMemberOfClass 的区别么?
深入理解iOS开发中的isa指针
iOS消息机制相关
iOS Runtime《一》 类Class 相关
解读objc_msgSend

你可能感兴趣的:(iOS知识加固(一)-Runtime篇)