runtime介绍
Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同 Objective-C的动态性是由Runtime API来支撑的 Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写
Runtime数据结构
objc_object
objc_class
-
class_rw_t结构
- class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容
-
class_ro_t结构
-
method_t结构
- method_t是对方法\函数的封装
- method_t是对方法\函数的封装
-
cache_t结构
- Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度
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指向
- 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中
-
如果最后还是没找到, 就会进入下一个阶段, 动态解析阶段
动态方法解析
-
当消息发送过程中,没有找到要调用的方法时, 就会进入动态方法解析阶段,
-
在动态方法解析过程中, 会根据类对象和元类对象进行判断, 分别处理
- 类对象调用resolveInstanceMethod:方法
- 元类对象调用resolveClassMethod:方法
-
我们可以在+ (BOOL)resolveInstanceMethod:(SEL)sel方法中, 使用Runtime添加其他方法的实现
消息转发
-
当动态方法解析也没有找到需要调用的方法实现时, 就会进入消息转发阶段。调用的是forwardingTargetForSelector:方法
- 可以在+forwardingTargetForSelector:方法中设置消息转发的对象
- 因为objc_msgSend的原理是给 消息接收者 发送 一条消息, 而这个消息是SEL类型的,并且不分是类方法(+)还是对象方法(-)
- 所以, 我们可以将消息接收者设置为实例对象
-
如果不实现+forwardingTargetForSelector:方法, 就会调用+methodSignatureForSelector:方法, 并调用+forwardInvocation:方法
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; }; 复制代码 - __rw_objc_super: 结构体
-
从实际代码中可以看到, 这两个成员变量分别传入了self和[Person class]
-
所以消息接收者是self, 从[Person class]中开始查找方法
super含义
- super的含义是, 查询方法的起点是父类, 不是本身的类对象
- 消息接收者是self, 不是父类对象
- 发送的消息是调用的方法
动态方法解析
- @dynamic
- 动态运行时语言将函数决议推迟到运行时。
- 编译时语言在编译期进行函数决议。
方法交换
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)