Runtime

47 阅读2分钟
  1. 数据结构
  2. 类对象与元类对象
  3. 消息传递
  4. 方法缓存
  5. 消息转发
  6. Method-Swizzling
  7. 动态添加方法
  8. 动态方法解析

数据结构

  • objc_object
  • objc_class
  • isa指针
  • method_t

objc_object(id = objc_object)

  • isa_t
  • 关于isa操作相关
  • 弱引用相关
  • 关联对象相关
  • 内存管理相关

objc_class

image.png

isa指针

  • 共用体isa_t image.png
  • isa指向
  • 关于对象,其指向类对象

image.png

  • 关于类对象,其指向元类对象

image.png

cache_t

  • 用于快速查找方法执行函数
  • 是可增量扩展的哈希表结构
  • 是局部性原理的最佳应用 image.png
class_data_bits_t
  • class_data_bits_t主要是对class_rw_t的封装
  • class_rw_t代表了类相关的读写信息、对class_ro_t的封装
  • class_ro_t代表了类相关的只读信息
class_rw_t

image.png

class_ro_t

image.png

method_t

image.png

Type Encodings
  • const char* types image.png
整体数据结构

image.png

对象、类对象、元类对象

  • 类对象存储实例方法列表等信息

  • 元类对象存储类方法列表

    image.png

笔试题
#import "Mobile.h"
@interface Phone : Mobile
@end

@implementation Phone

- (id)init
{
  self = [super init];
  if(self){
  NSLog(@"%@",NSStringFromClass([self class]));
  NSLog(@"%@",NSStringFromClass([super class]));
  }
  return self;
}
// 输出结果一致,均为Phone。

消息传递

  • void objc_msgSend(void /id self,SEL op,.../) image.png
  • void objc_msgSendSuper(void /*struct objc_super super,SEL op,.../)
struct objc_super {
 Specifies an instance of a class.
_unsafe\_unretained id reciver;(reciver->self)
}

image.png

image.png

缓存查找

  • 例:给定值是SEL,目标值是对应bucket_t中的IMP image.png

  • f(key) = key & mask

  • 当前类中查找

  • 对于已排序好的列表,采用二分查找算法查找方法对应函数执行
  • 对于没有排序的列表,采用一般遍历查找方法对应函数执行
父类逐级查找

image.png

消息转发

image.png 可以进行代码演示进行验证。

Method-Swizzling

image.png

代码演示

  • 导入runtime的头文件 #import <objc/runtime.h>
  • 获取方法
+ (void)load
{
Method test = class_getInstanceMethod(self,@selector(test));
Method otherTest = class_getInstanceMethod(self,@selector(otherTest));
交换方法
method_exchangeImplementations(test,otherTest);
}
  • 实际使用如:
  • 交换ViewController中的viewDidAppear等方法,添加相应的操作,如统计等。

动态添加方法

  • performSelector: 编译时没有方法,运行时才有的方法。主要考察runtime动态添加方法。
  • 动态添加test方法的实现
  • void testImp (void){}
  • class_addMethod(self,@selector(test),testImp,"v@:");

动态方法解析

@dynamic :

  • 标记为@dynamic则set,get方法,运行时决定
  • 动态运行时语言将函数决议推迟到运行时。
  • 编译时语言在编译期进行函数决议。

Runtime实战

  • [objc foo]和objc_msgSend()函数之间有什么关系?
  • runtime如何通过Selector找到对应的IMP地址的?(先找缓存,当前类方法,找继承类)
  • 能否向编译后的类中增加实例变量?(内存布局完成后,不能增加,ro不可更改)