Runtime相关

430 阅读7分钟

runtime介绍

Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同 Objective-C的动态性是由Runtime API来支撑的 Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写

Runtime数据结构

objc_object

img

objc_class

image.png

image.png

  • class_rw_t结构

    • class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容
    • img
  • class_ro_t结构 image.png

  • method_t结构

    • method_t是对方法\函数的封装 image.png
  • cache_t结构

    • Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度
    • imgimg

OC对象的分类

实例对象

instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对像。instance对象在内存中存储的信息包括

  • isa指针
  • 其他成员变量

类对象

我们平时说的类,其实也是对象,称为类对象, 每个类在内存中有且只有一个class对象。class对象在内存中存储的信息主要包括

  • isa指针
  • superclass指针
  • 类的属性信息(@property)
  • 类的对象方法信息(instance method)
  • 类的协议信息(protocol)
  • 类的成员变量信息(ivar)

元类对象

每个类在内存中有且只有一个meta-class对象。meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括

  • isa指针
  • superclass指针
  • 类的类方法信息(class method)

isa指向

image.png

  • instance的isa指向class
    • 当调用对象方法时, 通过instance的isa找到class, 最后找到对象方法的实现进行调用
  • class的isa指向meta-class
    • 当调用类方法时, 通过class的isa找到meta-class, 最后找到类方法的实现进行调用
  • meta-class的isa指向基类的meta-class

消息机制

OC的方法调用本质给方法调用者发送消息

消息传递

  • 当消息接收者为空时, 直接返回, 结束objc_msgSend函数的调用

  • 当消息接收者有值时, 查看缓存

  • 如果方法没有被缓存过, 就会查询方法列表

  • 当缓存中没有找到需要调用的方法时, 就会在方法列表中查找, 如果找到就会存到缓存cache中

  • 方法存在cls->rw->methods中, 而methods是个二位数组, 所以需要进行遍历查询, 这里先拿到一维数组调用search_method_list函数查询

  • 在一维数组中查找方法, 这里有两种情况

    • 第一种: 方法列表已经排好序, 会通过findMethodInSortedMethodList函数查找,findMethodInSortedMethodList函数使用的是二分查找的方式查询方法
    • 第二种: 方法列表没有排好序, 会一个一个遍历查找
  • 当找到方法后, 会先将方法存储到cache中

  • 如果在自己的类对象中没有找到需要调用的方法, 就会去查找父类中是否有该方法

    • 1.查找时会一层一层遍历所有父类, 只要某个父类中找到方法, 就会结束查找
    • 2.先从父类的缓存中找, 如果找到, 会先存到自己的cache中
    • 3.如果父类的缓存中没有该方法, 就会从父类的方法列表中查找, 如果找到就会存入到自己的cache中, 并不会存入到父类的cache中
    • 4.如果没找到, 就会通过for循环查看父类的父类中有没有方法, 依次类推, 只要找到就会结束查询, 并存到自己的cache中
  • 如果最后还是没找到, 就会进入下一个阶段, 动态解析阶段

    img

动态方法解析

  • 当消息发送过程中,没有找到要调用的方法时, 就会进入动态方法解析阶段,

  • 在动态方法解析过程中, 会根据类对象和元类对象进行判断, 分别处理

    • 类对象调用resolveInstanceMethod:方法
    • 元类对象调用resolveClassMethod:方法
  • 我们可以在+ (BOOL)resolveInstanceMethod:(SEL)sel方法中, 使用Runtime添加其他方法的实现

    img

    img

消息转发

  • 当动态方法解析也没有找到需要调用的方法实现时, 就会进入消息转发阶段。调用的是forwardingTargetForSelector:方法

    • 可以在+forwardingTargetForSelector:方法中设置消息转发的对象
    • 因为objc_msgSend的原理是给 消息接收者 发送 一条消息, 而这个消息是SEL类型的,并且不分是类方法(+)还是对象方法(-)
    • 所以, 我们可以将消息接收者设置为实例对象
  • 如果不实现+forwardingTargetForSelector:方法, 就会调用+methodSignatureForSelector:方法, 并调用+forwardInvocation:方法

    img

super

super原理

  • super的底层调用了objc_msgSendSuper方法, 并传入两个参数

    • __rw_objc_super: 结构体
      • receiver: 消息接收者
      • super_class: 从super_class开始查找调用的方法
    • sel_registerName("run"): 方法SEL
    struct objc_super {
       __unsafe_unretained _Nonnull id receiver;
       __unsafe_unretained _Nonnull Class super_class;
    };
    复制代码
    
  • 从实际代码中可以看到, 这两个成员变量分别传入了self和[Person class]

  • 所以消息接收者是self, 从[Person class]中开始查找方法

super含义

  • super的含义是, 查询方法的起点是父类, 不是本身的类对象
  • 消息接收者是self, 不是父类对象
  • 发送的消息是调用的方法

动态方法解析

  • @dynamic
    • 动态运行时语言将函数决议推迟到运行时。
    • 编译时语言在编译期进行函数决议。

方法交换

image.png

image.png

Runtime 应用

  • 方法交换(Method Swizzle)
  • 给分类(Category)添加属性
  • 实现类的自动归档和自动解档
  • 实现字典转模型

Runtime API

动态创建一个类(参数:父类,类名,额外的内存空间)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)

注册一个类(要在类注册之前添加成员变量)
void objc_registerClassPair(Class cls) 

销毁一个类
void objc_disposeClassPair(Class cls)

获取isa指向的Class
Class object_getClass(id obj)

设置isa指向的Class
Class object_setClass(id obj, Class cls)

判断一个OC对象是否为Class
BOOL object_isClass(id obj)

判断一个Class是否为元类
BOOL class_isMetaClass(Class cls)

获取父类
Class class_getSuperclass(Class cls)

成员变量

获取一个实例变量信息
Ivar class_getInstanceVariable(Class cls, const char *name)

拷贝实例变量列表(最后需要调用free释放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)

设置和获取成员变量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)

动态添加成员变量(已经注册的类是不能动态添加成员变量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)

获取成员变量的相关信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)
复制代码

属性

获取一个属性
objc_property_t class_getProperty(Class cls, const char *name)

拷贝属性列表(最后需要调用free释放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)

动态添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
                  unsigned int attributeCount)

动态替换属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
                      unsigned int attributeCount)

获取属性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)
复制代码

方法

获得一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)

方法实现相关操作
IMP class_getMethodImplementation(Class cls, SEL name) 
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2) 

拷贝方法列表(最后需要调用free释放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)

动态添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

动态替换方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)

获取方法的相关信息(带有copy的需要调用free去释放)
SEL method_getName(Method m)
IMP method_getImplementation(Method m)
const char *method_getTypeEncoding(Method m)
unsigned int method_getNumberOfArguments(Method m)
char *method_copyReturnType(Method m)
char *method_copyArgumentType(Method m, unsigned int index)

选择器相关
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)

用block作为方法实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
获得一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)

方法实现相关操作
IMP class_getMethodImplementation(Class cls, SEL name) 
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2) 

拷贝方法列表(最后需要调用free释放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)

动态添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

动态替换方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)

获取方法的相关信息(带有copy的需要调用free去释放)
SEL method_getName(Method m)
IMP method_getImplementation(Method m)
const char *method_getTypeEncoding(Method m)
unsigned int method_getNumberOfArguments(Method m)
char *method_copyReturnType(Method m)
char *method_copyArgumentType(Method m, unsigned int index)

选择器相关
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)

用block作为方法实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)