1 Runtime 简介
Runtime 是指在 iOS 操作系统上执行代码时所涉及的运行时环境。
Objective-C Runtime 是一个运行时库,提供了一系列的 C 语言 API,用于实现对象的创建、消息传递、方法调用、内存管理等重要功能。
Swift Runtime 提供了 Swift 语言的类型系统、内存管理、异常处理等基础功能。与 Objective-C Runtime 相比,Swift Runtime 的设计更加现代化,支持泛型、协议扩展、错误处理等 Swift语言特性。
静态语言(如 C语言)编译器在编译时就确定了函数调用的地址和参数。静态函数调用方式在一定程度上会比 Objective-C 的动态消息传递方式更高效,因为不需要在运行时动态查找方法实现,而是在编译时就确定了调用关系。
2 Runtime 机制
2.1 编译时
Objective-C 中使用 [object method] 这种方式调用方法,在编译后最终都会被转换成 objc_msgSend 函数的调用。
实例对象的调用方法
objc_msgSend(instenceClass, @selector(methodName));
类对象的调用方法
objc_msgSend(objc_getClass("CLASS"), @selector(methodName));
2.2 运行时
当向一个对象发送消息时,Runtime 会进行 消息发送 和 消息转发。
消息发送:当向一个对象发送消息时,Runtime 会根据对象所属类的方法列表查找对应的方法实现,如果找到则执行该方法,如果找不到则触发消息转发机制。
-
首先,通过
object的isa指针查找它的类对象objc_class; -
在类对象
objc_class的方法缓存objc_cache中查找方法选择器SEL; -
若不存在,则会在方法列表
objc_method_list中查找; -
如果还是没找到,则会沿着继承链向上到父类
super_class中查找; -
一旦找到方法选择器
SEL,就去执行它的方法实现IMP; -
仍然没找到的话,就会调用
_objc_msgForward(id, SEL, ...)进行消息转发。
消息转发:当 Runtime 在类的方法列表中找不到对应的方法实现时,会触发消息转发机制。通过动态方法解析、备用接收者和完整消息转发来处理消息,如果消息转发失败会导致程序崩溃。
消息转发机制分为三个步骤:
1. 动态方法解析:在对象接收到无法处理的消息后,首先会尝试动态添加方法实现。Runtime 会调用 +resolveInstanceMethod: 或 +resolveClassMethod: 动态地为类添加方法的实现。如果方法解析成功,Runtime 重新启动消息发送。
2. 备用接收者:如果方法解析失败,Runtime 会调用 -forwardingTargetForSelector: ,在这个方法中返回一个备用接收者对象,消息将被转发给该对象进行处理。
3. 完整消息转发:如果备用接收者未能处理消息,Runtime 会调用 -forwardInvocation: 方法,将原始的未知消息封装成 NSInvocation 对象,手动处理消息。
2.3 消息转发示例
调用方法的方式是使用 [实例 方法名] 或 [实例 方法名:参数]
私有方法调用,可使用 NSObject 的 performSelector 方法,只支持调用最多两个入参,并且入参类型和返回类型为 id 的方法。若入参的个数多于两个,可以使用 NSInvocation 来调用方法。
1. NSInvocation 调用示例
1. 假设有如下方法:
- (NSString *)getName:(NSString *)name andAge:(NSInteger)age {
return @"test";
}
2. 获取该方法的签名
NSMethodSignature *signature = [self methodSignatureForSelector:@selector(getName:andAge:)];
断点后,可看到有一个参数 _typeString 值为 @32@0:8@16q24
- @32 方法的返回类型
NSString * - @0和:8 方法自带的参数
self和_cmd - @16 自己设置的参数
(NSString *)name - q24 自己设置的参数
(NSInteger)age
3. NSInvocation 具体调用
SEL sel = @selector(getName:andAge:);
// 通过 NSObject 的实例方法来获取方法签名
NSMethodSignature *signature = [self methodSignatureForSelector:sel];
// 通过 NSObject 的类方法来获取方法签名
NSMethodSignature *signature = [self.class instanceMethodSignatureForSelector:sel];
// 通过方法类型字符串来获取方法签名
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"@32@0:8@16q24"];
// 通过方法类型字符串来获取方法签名
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"@@:@q"];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
// 防止参数被释放
[invocation retainArguments];
// 设置方法调用者
invocation.target = self;
// 设置方法选择器,这里的方法名一定要与方法签名类中的方法一致
invocation.selector = sel;
// 设置第2个参数的值 第0、1参数被方法的 self 与 _cmd 占用
NSString *name = @"xiaomi";
[invocation setArgument:&name atIndex:2];
// 设置第3个参数的值
NSInteger age = 20;
[invocation setArgument:&age atIndex:3];
// 调用方法
[invocation invoke];
// 获取方法的返回值,该方法需要在 invoke 之后调用,否则是 nil
NSString *returnValue;
[invocation getReturnValue:&returnValue];
// 获取第2个参数值
NSString *argument2;
[invocation getArgument:&argument2 atIndex:2];
// 获取第3个参数值
NSInteger argument3;
[invocation getArgument:&argument3 atIndex:3];
2. 消息转发示例
调用未知方法 [[MyClass new] unknownMethod]; 触发消息转发机制,具体处理如下:
1. 动态方法解析示例
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface MyClass : NSObject
@end
@implementation MyClass
// 动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
if (sel == @selector(unknownMethod)) {
IMP imp = class_getMethodImplementation(self, @selector(dynamicMethod));
Method method = class_getInstanceMethod(self, @selector(dynamicMethod));
const char *type = method_getTypeEncoding(method);
BOOL didAddMethod = class_addMethod(self, sel, imp, type);
return didAddMethod;
}
return [super resolveInstanceMethod:sel];
}
- (void)dynamicMethod {
NSLog(@"Dynamic method is called!");
}
@end
2. 快速转发-备用接收者重定向示例
#import <Foundation/Foundation.h>
@interface BackupHandler : NSObject
- (void)unknownMethod;
@end
@implementation BackupHandler
- (void)unknownMethod {
NSLog(@"Message handled by backup handler");
}
@end
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface MyClass : NSObject
@end
@implementation MyClass
// 动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
return NO; // 返回NO,进入下一步转发
}
// 备用接收者
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(unknownMethod)) {
if ([[BackupHandler new] respondsToSelector:aSelector]) {
return [BackupHandler new];
}
}
return [super forwardingTargetForSelector:aSelector];
}
3. 慢速转发-消息重定向示例
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
@interface MyClass : NSObject
@end
@implementation MyClass
// 动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
return NO; // 返回NO,进入下一步转发
}
// 备用接收者
- (id)forwardingTargetForSelector:(SEL)aSelector {
return nil; // 返回nil,进入下一步转发
}
// 方法签名,获取方法的参数和返回值类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(unknownMethod)) {
// 自定义方法的签名,进入 forwardInvocation:
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
// 消息重定向
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL aSelector = anInvocation.selector;
if ([self respondsToSelector:aSelector]) {
[anInvocation invokeWithTarget:self];
} else if ([[BackupHandler new] respondsToSelector:aSelector]) {
[anInvocation invokeWithTarget:[BackupHandler new]];
} else {
// 无法处理消息,崩溃
[self doesNotRecognizeSelector:aSelector];
}
}
@end
2.4 数据结构
objc_object: 表示实例对象结构体,是所有对象的基础结构,它定义了对象的通用属性和行为。
struct objc_object {
Class isa; // 指向类的指针,用于动态派发方法
};
objc_class: 表示类的结构体,用于描述类的属性和行为
struct objc_class {
Class isa; // 指向元类的指针
Class super_class; // 指向父类的指针
const char *name; // 类名
long version; // 类的版本信息
long info; // 类的附加信息
long instance_size; // 实例对象的大小
struct objc_ivar_list *ivars; // 实例变量列表
struct objc_method_list **methodLists; // 方法列表数组
struct objc_cache *cache; // 方法缓存
struct objc_protocol_list *protocols; // 协议列表
};
SEL: 表示方法选择器的结构体,包含方法名和类型编码。
// 定义消息发送相关的结构体
typedef struct objc_selector *SEL;
struct objc_selector {
char *name; // 方法名
char *types; // 类型编码
};
IMP: 方法实现的函数指针类型,用于指向具体的方法实现。
typedef id (*IMP)(id, SEL, ...);
objc_method_list: 方法列表结构体,包含一系列方法定义。
struct objc_method_list {
struct objc_method_list *next; // 指向下一个方法列表的指针
int count; // 方法数量
struct objc_method method_list[1]; // 实际方法数组
};
objc_method: 方法结构体,包含方法选择器、方法类型编码和方法实现。
struct objc_method {
SEL method_name; // 方法选择器
char *method_types; // 方法类型编码字符串
IMP method_imp; // 方法实现的函数指针
};
objc_cache: 方法缓存结构体,用于提高方法调用的性能。
struct objc_cache {
unsigned int mask; // 掩码,用于快速计算方法索引
unsigned int occupied; // 已占用的缓存槽数量
Method buckets[1]; // 方法缓存槽数组
};
objc_protocol_list: 协议列表结构体,包含一系列协议定义。
// 定义消息转发相关的结构体
struct objc_protocol_list {
struct objc_protocol_list *next; // 指向下一个协议列表的指针
long count; // 协议数量
Protocol *list[1]; // 实际协议数组
};
Message: 消息结构体,包含类和选择器,用于消息传递。
typedef struct {
Class isa;
SEL selector;
} *Message;
objc_msgSend_fp: objc_msgSend 函数指针类型,用于动态发送消息。
typedef id (*objc_msgSend_fp)(id self, SEL op, ...);
3 Runtime API
3.1 objc_ 函数
用于 Runtime API 的核心功能,如创建类、注册方法、消息传递等。
- 创建与销毁
-
objc_constructInstance:创建并返回给定大小的实例(ARC中无效)。 -
objc_destructInstance:释放对象的实例变量,并在必要时调用析构函数(ARC中无效)。
-
- 协议操作
-
objc_allocateProtocol:创建一个新的协议。 -
objc_registerProtocol:注册一个由objc_allocateProtocol创建的协议。 -
objc_getProtocol:获取指定名称的协议。 -
objc_copyProtocolList:获取当前进程中已注册的协议列表。
-
- 类与元类操作
objc_allocateClassPair: 创建一个新的类和其元类。objc_registerClassPair: 注册一个由objc_allocateClassPair创建的类。objc_disposeClassPair: 注销一个类并释放其占用的资源。objc_getClass: 通过类名获取已注册的类。objc_getMetaClass: 获取类的元类。objc_getClassList: 获取当前进程中已注册的类列表。objc_lookUpClass: 在当前进程中查找类。
- 关联对象操作
objc_getAssociatedObject: 获取与对象关联的对象。objc_setAssociatedObject: 设置与对象关联的对象。objc_removeAssociatedObjects: 移除与对象关联的对象。
3.2 class_ 函数
用于操作类的函数,如获取类名、获取类的父类、检查类是否遵循某个协议等。
- 类创建与销毁
class_createInstance: 创建类的实例。
- 类成员操作
class_copyIvarList: 获取类的实例变量列表。class_copyMethodList: 获取类的方法列表。class_copyPropertyList: 获取类的属性列表。class_copyProtocolList: 获取类遵循的协议列表。class_addIvar: 添加实例变量到类。class_addMethod: 添加方法到类。class_addProperty: 添加属性到类。class_addProtocol: 添加协议到类。class_getInstanceVariable: 获取类的实例变量。class_getClassVariable: 获取类的类变量。class_setIvarLayout: 设置类的实例变量布局。class_getWeakIvarLayout: 获取类的弱引用实例变量布局。class_getInstanceMethod: 获取类的实例方法。class_getClassMethod: 获取类的类方法。class_getMethod: 获取类的方法。class_getMethodImplementation: 获取方法的实现。class_getProperty: 获取类的属性。class_replaceMethod: 替换类的方法。class_getIvarLayout: 获取类的实例变量布局。class_setWeakIvarLayout: 设置类的弱引用实例变量布局。class_setSuperclass: 设置类的父类。
- 类信息获取
class_getName: 获取类的名称。class_getSuperclass: 获取类的父类。class_getInstanceSize: 获取类的实例大小。class_getVersion: 获取类的版本号。class_setVersion: 设置类的版本号。class_isMetaClass: 判断类是否为元类。
- 类行为判断
class_respondsToSelector: 判断类是否响应特定的选择器。class_conformsToProtocol: 判断类是否遵循特定协议。
3.3 object_ 函数
用于操作实例对象,如获取和设置对象的类、复制对象、管理对象的关联对象等。
- 实例对象创建与销毁
object_copy: 复制一个对象(ARC中无效)。object_dispose: 释放一个对象占用的内存(ARC中无效)。
- 实例对象操作
object_setInstanceVariable: 设置对象的实例变量(ARC中无效,使用object_setIvar)。object_getInstanceVariable: 获取对象的实例变量(ARC中无效,使用object_getIvar)。object_setIvar: 设置对象的实例变量或对象的属性。object_getIvar: 获取对象的实例变量或对象的属性。object_setIvarWithStrongDefault: 设置对象的实例变量,并采用默认的强引用方式。object_getIndexedIvars: 获取对象的索引实例变量。object_setField: 设置对象的字段。object_getField: 获取对象的字段。
- 实例对象信息获取与设置
object_getClassName: 获取对象的类名。object_setClass: 修改对象的类。object_getClass: 获取对象的类。object_isClass: 判断对象是否为类。
3.4 ivar_ 函数
用于操作实例变量,包括获取实例变量的列表、类型、偏移量等。
- 实例变量信息获取
ivar_getName: 获取实例变量的名称。ivar_getTypeEncoding: 获取实例变量的类型编码。ivar_getOffset: 获取实例变量的偏移量。
3.5 method_ 函数
用于操作方法,如获取类的方法列表、获取方法的选择器、动态添加方法等。
- 方法属性获取
method_getName: 获取方法的名称。method_getImplementation: 获取方法的实现。method_getTypeEncoding: 获取方法的类型编码。method_getDescription:获取方法的描述。
- 方法参数信息获取
method_getNumberOfArguments: 获取方法的参数数量。method_getArgumentType: 获取方法的参数类型。method_copyArgumentType: 复制方法的参数类型。method_getReturnType: 获取方法的返回类型。method_copyReturnType: 复制方法的返回类型。
- 方法行为修改
method_exchangeImplementations: 交换两个方法的实现。method_setImplementation:设置方法的实现。
3.6 property_ 函数
用于操作属性,包括获取类的属性列表、属性的特性、属性的名称等。
- 属性信息获取
property_getName: 获取属性的名称。property_getAttributes: 获取属性的特性列表。
- 属性特性信息获取
property_copyAttributeList: 复制属性的特性列表。property_copyAttributeValue: 复制属性的特定特性的值。
3.7 protocol_ 函数
用于操作协议,如获取已注册的协议列表、获取协议的名称、检查类是否实现了某个协议等。
- 协议信息获取
protocol_getName: 获取协议的名称。
- 协议方法管理
protocol_getMethodDescription: 获取协议方法的描述。protocol_copyMethodDescriptionList: 复制协议方法的描述列表。
- 协议属性管理
-
protocol_copyPropertyList: 复制协议的属性列表。 -
protocol_addProperty:向协议中添加属性。 -
protocol_getProperty:获取协议中的属性。
-
- 协议列表管理
protocol_copyProtocolList: 复制协议遵循的协议列表。protocol_conformsToProtocol: 判断一个协议是否遵循另一个协议。
- 协议操作
protocol_addProtocol:向协议中添加另一个协议。protocol_addMethodDescription:向协议中添加方法描述。protocol_isEqual:判断两个协议是否相等。
3.8 sel_ 函数
用于操作方法选择器,包括创建方法选择器、将方法选择器转换为字符串等。
- 选择器创建
sel_registerName: 根据字符串注册一个选择器。
- 选择器信息获取
sel_getName: 获取选择器的名称。sel_getUid: 获取选择器。
- 选择器操作
sel_isEqual: 判断两个选择器是否相等。
3.9 imp_ 函数
用于操作方法的实现,如获取方法的实现、设置方法的实现等。
- 函数实现
imp_implementationWithBlock: 使用Block创建一个函数实现。imp_getBlock: 获取函数实现中的Block。imp_removeBlock: 移除函数实现中的Block。
3.10 cache_ 函数
用于操作方法缓存,如获取类的方法缓存、清空方法缓存等。
- 方法缓存
cache_getImp: 获取方法缓存中的实现。cache_setImp: 设置方法缓存中的实现。
3.11 message_ 函数
用于消息传递相关的函数,如向对象发送消息、消息转发等。
- 消息发送
message_send: 发送消息给对象。message_sendSuper:发送消息给父类的实现。
4 开发问题
4.1 向 nil 对象发送消息将会发生什么?
向 nil 对象发送消息时,实际上并不会发生任何异常或错误,而是会返回一个默认值,具体取决于返回类型。
消息的返回类型:
- 返回对象类型的消息,返回值将会是 nil
- 返回基本数据类型的消息,返回值将会是 0 或者相应的默认值
- 返回结构体的消息,返回值将会是一个空的结构体
4.2 对象的 isa 指针?
1. 对象关系:
- 对象的
isa指针 指向类对象 - 类对象的
isa指针 指向元类对象 - 元类对象的
isa指针 指向根元类(root class's metaclass) - 根元类的
isa指针 指向自身 superclass指针 指向当前类对象的父类的类对象
2. 对象结构:
- 实例对象结构体中只包含一个
isa指针,指向该实例对象所属的类 - 类对象的结构体包含
isa、superclass、方法、属性、协议列表以及成员变量的描述等信息
3. 调用方法机制:
- 所有对象调用方法的方式相同,方法的定义存储在类对象中,因为类对象只有一个,所以方法可以存储在类对象中,而不需要存在于每个实例对象中
4. isa 指针作用:
- 确定对象的类: 通过
isa指针,确定对象的类型,从而确定该类定义的方法和访问属性 - 实现消息传递: 通过
isa指针,确定对象的类型,然后在类中查找对应的方法实现进行调用 - 支持继承和多态:
isa指针的存在支持了面向对象编程中的继承和多态特性。子类对象的isa指针指向其父类或者实现的协议,从而可以实现方法的重写和动态绑定
4.3 _objc_msgForward 函数作用,直接调用它将会发生什么?
_objc_msgForward 函数用于处理消息转发(Message Forwarding)。当 OC 对象接收到一个无法识别的消息时,Runtime 会调用 _objc_msgForward 函数来进行消息转发,以实现动态方法解析或消息转发到其他接收者。
直接调用 _objc_msgForward 函数通常会导致编译器错误或者运行时异常。正确的做法是,当需要实现自定义的消息转发逻辑时,可以通过重写 OC 对象的 forwardInvocation: 方法或者动态添加方法来实现消息转发,而不是直接调用 _objc_msgForward 函数。
4.4 class_rw_t 和 class_ro_t 的区别
class_ro_t 存储了当前类在编译期就已经确定的属性、方法以及遵循的协议,里面是没有分类的方法的。那些运行时添加的方法将会存储在运行时生成的 class_rw_t 中。
ro 即表示 read only,是无法进行修改的。
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name;
method_list_t * baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
ObjC 类中的属性、方法还有遵循的协议等信息都保存在 class_rw_t 中。
rw 代表可读可写。
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro; // 指向只读的结构体,存放类初始信息
/*
这三个都是二维数组,是可读可写的,包含了类的初始内容、分类的内容。
methods中,存储 method_list_t ----> method_t
二维数组,method_list_t --> method_t
这三个二位数组中的数据有一部分是从 class_ro_t 中合并过来的。
*/
method_array_t methods; // 方法列表(类对象存放对象方法,元类对象存放类方法)
property_array_t properties; // 属性列表
protocol_array_t protocols; //协议列表
Class firstSubclass;
Class nextSiblingClass;
//...
}
两者区别在于:
class_ro_t 是在编译期间确定的;
class_rw_t 是在运行时确定的,Runtime 在调用的 realizeClass 方法时,会生成 class_rw_t 结构体,先将 class_ro_t 的内容拷贝过去,然后再将当前类的分类的属性、方法等拷贝到其中。
所以可以说 class_rw_t 是 class_ro_t 的超集,当然实际访问类的方法、属性等也都是访问的 class_rw_t 中的内容
4.5 method swizzling
1. 定义
方法交换,用于在运行时动态交换两个方法的实现。
2. 基本原理
通过修改方法的实现指针,将一个方法的实现替换为另一个方法的实现,从而达到动态改变方法行为的目的。
3. 实现方式
method_exchangeImplementations: 交换两个方法的实现
class_replaceMethod: 替换方法的实现
method_setImplementation: 直接设置某个方法的实现
4. 注意事项
唯一性:在 +load 方法中,不在 +initialize 方法中改变方法实现。这是因为 +initialize 可能会被子类所继承并重新执行,而 load 并不会被继承。
原子性:使用 dispatch_once 来执行方法交换,这样可以保证只运行一次。
5. 示例
#import <objc/runtime.h>
@implementation MyClass
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// case 1: 替换实例方法
Class class = [self class];
// case 2: 替换类方法
Class class = object_getClass([self class]);
// 源方法的 SEL 和 Method
SEL originalSelector = @selector(originalMethod);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
// 交换方法的 SEL 和 Method
SEL swizzledSelector = @selector(swizzledMethod);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 尝试给类添加新方法,如果已存在同名方法则替换
BOOL didAddMethod = class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
// 如果方法已存在,则直接交换两个方法的实现
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)originalMethod {
NSLog(@"Original method implementation");
}
- (void)swizzledMethod {
NSLog(@"Swizzled method implementation");
}
@end
4.6 weak 底层实现原理?知道 SideTable 吗?
实现原理
- 初始化时:Runtime 会调用
objc_initWeak函数,初始化一个新的 weak 指针指向对象的地址。 - 添加引用时:
objc_initWeak函数会调用objc_storeWeak()函数,其作用是更新指针指向,创建对应的弱引用表。 - 释放时:调用
clearDeallocating函数。clearDeallocating函数首先根据对象地址获取所有 weak 指针地址的数 组,然后遍历这个数组把其中的数据设为 nil,再把这个 entry 从 weak 表中删除,最后清理对象的记录。
SideTable 结构体是负责管理类的引用计数表和 weak 表。
struct SideTable {
spinlock_t slock;
RefcountMap_t refcnts;
weak_table_t weak_table;
};
SideTable 是用于存储 NSMutableDictionary、NSMutableSet 和 NSCache 等可变集合类的底层数据结构。它的定义在 objc-runtime-new.h 中,是 OC Runtime 中的私有结构体。
-
spinlock_t slock:用于保护SideTable的读写操作的自旋锁。自旋锁是一种基本的同步机制,它在尝试获取锁时会一直循环(自旋)直到获得锁为止,适用于对资源的访问时间很短的情况。 -
RefcountMap_t refcnts:是一个objc_object到引用计数值的映射表。在 OC 的引用计数管理中,每个对象都有一个引用计数,用于追踪对象的生命周期。RefcountMap_t是一个哈希表,用于存储对象的引用计数,以及一些额外的标志信息,例如是否是弱引用。 -
weak_table_t weak_table:是一个用于存储弱引用的哈希表。在 OC 中,如果一个对象被多个弱引用指向,这些弱引用的地址会被存储在weak_table中。当对象释放时,SideTable的weak_table中与该对象相关的所有弱引用都会被置为nil,以避免野指针问题。
Weak 表是一个 hash(哈希)表,Key 是 weak 所指对象的地址,Value 是 weak 指针的地址(所指对象指针的地址)数组。
当 weak 引用指向的对象被释放时,又是如何去处理 weak 指针的呢?
当释放对象时,其基本流程如下:
- 调用 objc_release
- 因为对象的引用计数为 0,所以执行 dealloc
- 在 dealloc 中,调用了_objc_rootDealloc 函数
- 在_objc_rootDealloc 中,调用了 object_dispose 函数
- 调用 objc_destructInstance
- 最后调用 objc_clear_deallocating
objc_clear_deallocating 调用流程
- 从 weak 表中获取废弃对象的地址为键值的记录
- 将包含在记录中的所有附有 weak 修饰符变量的地址,赋值为 nil
- 将 weak 表中该记录删除
- 从引用计数表中删除废弃对象的地址为键值的记录