相关资料引用
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008048-CH1-SW1
https://developer.apple.com/videos/play/wwdc2020/10163/
Objective-C一门动态语言, 它能在编译的时候转换为对应的C函数, 而消息的接受者 (reciever)
和接受者执行的方法 selector
, 只有在函数运行时才能确定, 所以说它是一门运行时语言. 通过在消息发送之前对函数的两个参数 recever
(具体的执行对象)和 selector
(执行的方法)进行不同的处理, 就可以实现各种黑魔法效果, 常见的有方法实现体(IMP)替换, 消息转发(Forward message), 同时为了配合运行时的特性, 它为每个方法和类型定义一套编码规则, Objective-C的对象和类型可以转换为Rumtime中指定的类型或这是标志符号, 保证他们都能有一套映射标准。
Runtime一共有2个版本, Objective-C 1.0和Objective-C 2.0, 它们的一个主要的区别就是:
旧版本运行时中,如果更改类中实例变量的布局,则必须重新编译从该类继承的类。
新版本运行时中,如果更改类中实例变量的布局,则不必重新编译继承自该类的类。
支持平台包括 iOS
, OS X
, 而这些仅使用于苹果相关的应用
通过Objective-C源代码;
通过在基础框架的NSObject类中定义的方法;
通过直接调用运行时函数。
在大多数情况下运行时胸在后台自动工作, 只需要编写和编译源代码就能使用它, 通常这些都是由Xcode来完成, 编译包含Objective-C类和方法的代码时,编译器将创建实现语言动态特性的数据结构和函数调用。数据结构捕获类和类别定义以及协议声明中的信息;它们包括在用Objective-C编程语言定义类和协议时讨论的类和协议对象,以及方法选择器、实例变量模板和从源代码中提取的其他信息。主运行时函数是发送消息的函数,如消息传递中所述。它由源代码消息表达式调用。
在Cocoa中的绝大部分对象都是基于 NSObject
生成的子类, 因此大部分类都继承了它的基本方法(NSProxy是一个另外), 同时也提供依稀诶抽象的模版方法, 如 description
, 则需要子类去实现. 因为每个类的具体内容不大一样, 主要是为了方便 GDB print object命令
, 默认情况下它只包括一个类名称和地址。部分的 NSObject
方法只是查询运行时的系统信息, 这些方法可以对类型进行安全性检查
isKindOfClass
isMemenberOfClass
respondsToSelecctor
cconformsToProtocol
methodForSelector
Swift工程中的api介绍
Source Code
提供了封装的函数调用,包括获取类的信息,添加类,获取实例对象的相关信息,获取类的定义,实例变量操作,对象关联,方法操作,库操作,方法选择器操作,协议操作,属性操作,还有 Objective-C
语言特有的功能,如bloc,weak操作。
[Object method]
编译之后它是 objc_msgSend(receiver, selector)
这样的,此外还有 objc_msgSend(receiver, selector, arg1, arg2, ...)
带参数发送.Object
作为 recevier
, method
作为 selector
参数.
首先它会在 recevier(object)中查找
selector`(方法选择器)的函数实现体,
然后找到之后根据换入的参数执行该方法
最后将函数的返回值作为自己的返回值
reciever
的类和它的结构,一个类主要包括2个关键的结构父类的指针
一个class的调度表,此表中的维持了一个方法选择器和它的实现函数关联的索引. selector-address
在编译时, reciver
和 selector
会自动被捕获做为 objc_msgSend
的两个参数,在代码中可以通过提供的关键字 _cmd
代表当前代码执行的方法.里用这些特性可以实现对方法和对象的校验,下面的官方simple给出的一个例子,它用于校验当前的method是否为 strange
.以及target是否为当前对象本省.
* strange {
id target = getTheReceiver();
SEL method = getTheMethod();
if ( target == self || method == _cmd )
return nil;
return [target performSelector:method];
}
规避动态绑定的唯一方法是获取方法的地址, 然后像调用函数一样去调用它, 当一个特定的方法被连续执行很多次的时候, 动态调用就会带来一定消息转发的开销, 会比较浪费性能, 我们可以通过获取函数地址直接调用, 这样和可以节省消息转发的开销
void (*setter)(id, SEL, BOOL);
int i;
setter = (void (*)(id, SEL, BOOL))[target
methodForSelector:@selector(setFilled:)]; //methodForSelector由Runtime System提供,获取具体的方法地址,及函数指针
for ( i = 0 ; i < 1000 ; i++ ) {
setter(targetList[i], @selector(setFilled:), YES); //通过直接执行函数,效率更高,减少循环带来的开销
}
Objective-C Runtime
提供了动态属性的的申明指令( @dynamic
dirctive)。它会在编译的时候告诉编译器需要动态的查找和解析该属性.此外还可以通过实现以下2个方法来动态解析对应的 selector
.@implementation MyClass
* (BOOL)resolveInstanceMethod:(SEL)aSEL //这里也可以是实例方法,当这个类没有对应 `selector` 实现的时候就会找到这个方法,通过拦截它就可以对该方法做一些默认的处理.
{
if (aSEL == @selector(resolveThisMethodDynamically)) {
//为该方法做一些默认的实现操作,将它的实现写在 `dynamicMethodIMP` 方法中
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSEL];
}
void dynamicMethodIMP(id self, SEL _cmd) {
// implementation ....
}
@end
完整执行流程如下
resolveInstanceMethod
方法ResolveInstanceMethod
forwardingTargetForSelector
methodSignatureForSelector
methodSignature
则执行forwardInvocation
Objective-C程序可以在运行时加载和链接新的类和类别。新代码被合并到程序中,并与开始时加载的类和类别相同。
在Cocoa环境中,通常使用动态加载来定制应用程序,其他人编译的程序在运行时加载的模块,就像 Interface Builder
加载自己定义的调色板和OSX新系统首选项应用程序加载自定义的首选模块以下.
尽管一个运行时函数可以在Mach-O文件中(objc_loadModules,在objc/objcload.h中定义)中执行 Objective-C
模块动态加载,但是 Cocoa
的NSBundle类为动态加载提供了一个非常方便的接口,该接口面向对象并与相关的服务集成,可以参考 NSBundle
的相关使用方法,通过NSBundle我们可以动态的加载 framework
而不必在启动时加载,可以大幅度的减少冷启动的时候,但也有它自己的弊端,很多的方法需要对 selector
进行减少以防程序崩溃,使用时需要根据具体的场景考量。
常用的方法如下:
* (NSArray *)allBundles
* (NSArray *)allFrameworks
* (NSBundle *)bundleForClass:(Class)aClass
* (BOOL)load //动态的加载bundle的可执行文件代码到引用程序中
* (NSString *)localizedStringForKey:(NSString *)key value:(NSString *)value table:(NSString *)tableName
想不处理消息的对象发送错误的消息时是错误的。但是, 在宣布错误之前, 运行时系统会给接收对象第二次处理消息的机会。
当发送一条消息给它不能处理的对象时, Runtime会将该消息内容进行包装, 然后调用对象的 forwardInvocation:
方法, 消息的参数是一个 NSInvocation
对象, 可以在当前对象中实现 forwardInvocation:
方法, 对这条不能处理的消息进行拦截和其他处理, 或者返回一个默认的信息, 在 NSObject
中有定义了 forwardInvocation:
方法的实现, 然鹅它只是简单的调用了 doesNotRecognizeSelector:
并会抛出一个错误, 继承于 NSObject
的子类通过重写 forwardInvocation:
这个方法可以对转发的消息进拦截。
* (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([someOtherObject respondsToSelector:
[anInvocation selector]])
[anInvocation invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
利用消息转发这一特性,可以实现多重继承。当子类本身继承某个类之后它还想实现其它类的方法,那就可以通过 forwardInvocation:
来实现消息转发。可以利用 NSProxy
来封装一个通用类,将该类继承的其他对象的方法做一个封装
NS_ROOT_CLASS
@interface NSProxy {
Class isa;
}
* (id)alloc;
* (id)allocWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE;
* (Class)class;
* (void)forwardInvocation:(NSInvocation *)invocation;
* (NSMethodSignature *)methodSignatureForSelector:(SEL)sel;
* (void)dealloc;
* (void)finalize;
* (NSString *)description;
* (NSString *)debugDescription;
* (BOOL)respondsToSelector:(SEL)aSelector;
* (BOOL)allowsWeakReference NS_UNAVAILABLE;
* (BOOL)retainWeakReference NS_UNAVAILABLE;
// - (id)forwardingTargetForSelector:(SEL)aSelector;
@end
* (void)forwardInvocation:(NSInvocation *)anInvocation
{
[proxy forwardInvocation: anInvocation];
}
在对象转发消息之前, 还需要实现这个方法, 因为 NSInvocation
需要依赖于 NSMethodSignature
来创建。它的类方法是 + invocationWithMethodSignature:
, 自带一个 NSMethodSignature
参数。 NSMethodSignature
是对Objective-C的方法参数进行编码。
* (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [surrogate methodSignatureForSelector:selector];
}
return signature;
}
//包装消息
...
NSMethodSignature *signature = [[self class] instanceMethodSignatureForSelector:aSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = self; //参数一
invocation.selector = aSelector; //参数二
invocation setArgument:&argument atIndex:2 //参数三
... //其它参数
...
为了帮助运行时系统,编译器为每个方法的返回和参数类型进行了编码,并将编码后的字符串于方法选择器进行关联.通过关键字 @encode(type)
注解来实现,它的返回值是一个字符串,runtime定义了他们的每个编码类型如下列表所示,我们也可以通过在工程中使用 print(%s), @encode(type))
来确认编码后的结果.
Code | Meaning |
---|---|
c | char |
i | int |
s | short |
l | long |
q | |
C | unsigned char |
I | unsigned int |
S | unsigned short |
L | unsigned long |
Q | unsigned long long |
f | float |
d | double |
B | C++ bool or a C99 _Bool |
v | void |
* | char*, 一个字符串 |
@ | 一个objet,id对象类型 |
# | 一个Class对象 |
: | 一个方法选择器(SEL) |
arry type | 一个数组 |
{name=type…} | 结构体 |
bnum | bit大小的枚举 |
^type | 一个子针类型 |
? | 一个未知类型,除此之外也用于函数指针 |
特殊类型: {examplestruct=@*i}
, {NSObject=#}
,类型大都比较有规律,按照首字母和大写来区分。
Objective-C是一种独特的计算机语言,以其独特的方括号表达式让很多学习其他的语言的开发者都很难理解,但它的本质是为了提供复杂的面向对象编程,在早起的C语言中提供的对象只有结构体,和数组,使用操作起来不是很方便,它是对C语言的补充,并有一套自己特有的编译器clang,lvvm旨在打造高效率的面向对象编程语言.并以一种简单明了的方式实现,基本上所有的语法操作都是为绕这 sendMessage来展开的。
主要包括以下几个部分,在此链接中可以找到具体的定义和例子
Objects, Classes, and Messaging
Defining a Class
Protocols
Declared Properties
Categories and Extensions
Associative References
Fast Enumeration
Enabling Static Behavior
Selectors
Exception Handling
Threading
Objective-C之所以被称为运行时语言,是因为它在编译时会翻译成对应的C函数,下面通过它的翻译构建过程来看看它的具体实现过程层.
在 ViewController.m
中随便写些代码,然后执行运行,找到 Xcode
的构建历史记录可以看到如下消息从上面的构建记录可以看出,它主要是通过 clang
来完成代码的编译组装成最终的可以执行文件 (.o)
, clang在编译时指定了一系列的参数,runtime环境配置,语法检查,中间编译产物的记录。
clang包含了很多的命令,下面也只是构建中的部分,具体可以参考 clang帮助文档,网上也有很多中文的解释,不过还是得结合具体的应用场景来理解。
...XcodeDefault.xctoolchain/usr/bin/clang //编译器的执行文件路径
-x objective-c // 指定编译文件的语言
-target x86_64-apple-ios12.0-simulator //指定需要生成code target,会根据这个arm指令生成查找对应的汇编
-fmessage-length=0 //只是为了控制控制台输出换行打印好看而已
-fdiagnostics-show-note-include-stack //诊断检查信息的提示包括对应的栈信息,提示用
-fmacro-backtrace-limit=0 //限定堆栈信息
-std=gnu11 //指定编译的语言版本
-fobjc-arc //开启Arc,buildSettings有指定,iOS5之后就支持了
-fmodules //Enable the 'modules' language feature,开启指定module的语言支持
-gmodules //Generate debug info with external references to clang modules or precompiled headers,生成clang modules或预编译头文件的debug信息
...
-DDEBUG=1 //是否是debug环境
-DOBJC_OLD_DISPATCH_PROTOTYPES=0
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.7.sdk // Set the system root directory (usually /)
....
-MMD -MT dependencies -MF /x86_64/ViewController.d //将依赖输出到指定的 ViewController.d文件,记录的依赖文件的路径
--serialize-diagnostics /x86_64/ViewController.dia //诊断文件
-c ../ViewController.m //预处理编译组装,不会自动执行run
-o ../x86_64/ViewController.o //输出最终的编译产物文件
-c
命令, 直接输出了目标文件, 要想查看生成的中间文件则需要通过 -rewrite-objc
命令查看, 结合上面的构建步骤挑选出几个必选的命令clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-12.0.0 -isysroot /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk ViewController.m
, 实际这个文件一般不会产生, 不过它可以更好的帮助我们理解runtime的实现过程, 探究Runtime底层实现必不可少的工具, 下面是翻译之后的 Objective-C
(简称OC)代码, 它编程了C++的文件, 二原来用 []
和 Objective-C
写的那部分代码全部编程了C函数。
...
//用于定义类和类对象的结构
struct __rw_objc_super {
struct objc_object *object;
struct objc_object *superClass;
__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}
};
// ViewController: controller对象本身
// _cmd代表当前viewController需要执行的方法
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
//这个函数执行分为以下几个步骤
//1. 创建了 `__rw_objc_super` 的结构体对象,参数1: viewController实例self,ViewController class
//2. 并向 ViewController中注册了一个 `viewDidLoad` 方法
//3. 返回了2个参数: self对应的 `__rw_objc_super` 结构体 和 `viewDidLoad` 的 `SEL`
//4. __rw_objc_super查找viewDidLoad具体实现并执行
((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper) ((__rw_objc_super) {
(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))
}, sel_registerName("viewDidLoad"));
}
...
Objective-C
的基类,封装了runtime最基本的实现,暴露了runtime
最常用的接口,类和对象的安全检查以及消息转发,它包含了一个isa
指针.所以对于任何继承于NSObject
的子类,它们都可以转化为一个objc_class
类型,它们的实例对象就是objc_class
的结构体指针,相应的方法,属性,协议都能在objc_class
和它相关的属性中找到.OBJC_ROOT_CLASS
OBJC_EXPORT
@interface NSObject {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
}
Class
是一个objc_class
结构体指针类型,在新版本的runtime中,它继承于objc_object
,内部提供一个class_rw_t
用来记录类初始化中会更新的数据(用class_data_bits_t
来保存),很多关键属性通过RO_
和RW_
开头的宏定义变量来定义,对于比较常的方法通过申明放式在外部单实现,它本省只负责管理一些class基本的操作,内存分配,类型检查,而关于它的方法,属性,协议相关的属性定义class_rw_t
中间,由class_data_bits_t
专门负责读写。`typedef struct objc_class *Class;`
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
//保存`class_rw_t`的信息(read and write), 以下方法都是通过`bits`来进行读写更新`class_rw_t`的相关信息
class_data_bits_t bits;
class_rw_t *data() { ..
void setData(class_rw_t *newData) { ...
void setInfo(uint32_t set) { ...
void clearInfo(uint32_t clear) { ...
void changeInfo(uint32_t set, uint32_t clear) { ...
//引用计数相关的方法检查设置 retain/release/autorelease/retainCount/
bool hasCustomRR() {
void setHasDefaultRR() { //设置默认的引用计数方法
void setHasCustomRR(bool inherited = false);
void printCustomRR(bool inherited);
//检查和设置 alloc/allocWithZone
bool hasCustomAWZ() { ...
void setHasDefaultAWZ() { ...
void setHasCustomAWZ(bool inherited = false);
void printCustomAWZ(bool inherited);
//requires raw isa
bool instancesRequireRawIsa() { ..
void setInstancesRequireRawIsa(bool inherited = false);
void printInstancesRequireRawIsa(bool inherited);
...
//ARC环境检测
bool hasAutomaticIvars() { ...
bool isARC() { ...
//对象关联
bool instancesHaveAssociatedObjects() { ...
void setInstancesHaveAssociatedObjects() { ...
//是否允许增加缓存
bool shouldGrowCache() { return true;
bool isInitializing() { return getMeta()->data()->flags & RW_INITIALIZING ...
void setInitializing() { ISA()->setInfo(RW_INITIALIZING);
bool isInitialized() { return getMeta()->data()->flags & RW_INITIALIZED;
void setInitialized();
//抽象的实现
bool isLoadable() { return true; ...
//方法申明,代码解耦
IMP getLoadMethod();
// Locking: To prevent concurrent realization, hold runtimeLock.
bool isRealized() { return data()->flags & RW_REALIZED;
// Returns true if this is an unrealized future class.
// Locking: To prevent concurrent realization, hold runtimeLock.
bool isFuture() { return data()->flags & RW_FUTURE; ...
//元类检测
bool isMetaClass() { ... return data()->ro->flags & RO_META; ...
//元类指向它本身,元类就是`objc_class`
// NOT identical to this->ISA when this is a metaclass
Class getMeta() {
if (isMetaClass()) return (Class)this;
else return this->ISA();
}
//当`objc_class`没有super时候的时候它就是`RootClass`
bool isRootClass() {
return superclass == nil;
}
bool isRootMetaclass() {
return ISA() == (Class)this;
}
//获取class名称
const char *mangledName() {
// fixme can't assert locks here
assert(this);
if (isRealized() || isFuture()) {
return data()->ro->name;
} else {
return ((const class_ro_t *)data())->name;
}
}
//
const char *demangledName(bool realize = false);
const char *nameForLogging();
uint32_t unalignedInstanceStart() {
assert(isRealized());
return data()->ro->instanceStart;
}
// 指定指针对齐方式
uint32_t alignedInstanceStart() { ...
uint32_t unalignedInstanceSize() { ...
uint32_t alignedInstanceSize() { ...
//指定初始化对象的大小,至少要求16字节以上
size_t instanceSize(size_t extraBytes) { ...
void setInstanceSize(uint32_t newSize) { ...
void chooseClassArrayIndex();
// 设置类索引
void setClassArrayIndex(unsigned Idx) { ...
unsigned classArrayIndex() { ...
};
class_rw_t
: 维护一个class经常需要变动的内容//添加propterties/protocols/methods时机
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
rw->methods.attachLists(mlists, mcount); ...
rw->properties.attachLists(proplists, propcount); ...
rw->protocols.attachLists(protolists, protocount); ...
...
static void methodizeClass(Class cls){ ...
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
//一个只读的class信息管理
const class_ro_t *ro;
//定义动态更新的方法,属性和协议
method_array_t c;
property_array_t properties;
protocol_array_t protocols;
//subclass记录,放便数据的快速读取
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
//flags修改和清除
void setFlags(uint32_t set)
void clearFlags(uint32_t clear)
void changeFlags(uint32_t set, uint32_t clear) ...
};
class_ro_t
: 只读class结构体,提供了class load时最原始的信息struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
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;
}
};
struct cache_t {
struct bucket_t *_buckets;
mask_t _mask; //用于控制缓存的扩容
mask_t _occupied; //记录方法的数量(_buckets缓存数量)
public:
//cache更新
...
};
struct bucket_t { //主要负责记录常用的方法并缓存
private:
// IMP-first is better for arm64e ptrauth and no worse for arm64.
// SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
MethodCacheIMP _imp;
cache_key_t _key;
#else
cache_key_t _key;
MethodCacheIMP _imp;
#endif
AssociationsHashMap
来实现的。同样采用了DISGUISE
来包装object指针,防止误检测内存泄漏void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) { ...
//hash_map
AssociationsHashMap &associations(manager.associations());
}
Tagged Point是苹果为了提高简单数据类型分配效率和节省内存开销而诞生的,举个栗子64位的空间中如果保存一个大小只占有32位或者空间更小的数据,这个数据是用指针表示,指针表示一个引用,指针比数据的内存大的多,这样通过指针访问就有点得不尝失了,所以就干脆直接把数据保存到指针中.通过对指针的内存空间重新layout得到tagged point
用一个标志位tag
来表示数据类型,
payload来指定它所容纳的数据
extended为扩展字段
适用于NSNumber、NSDate,小的NSString等类型,从它的内存分布来看.runtime根据targetPoint判断可以直接对number等小的对象且为targetPointer数据结构进行赋值,节省value的查找时间,比如NSNumber initWithInt
可以直接设置数据到指针上.
__attribute__
关键字Attribute官方文档介绍
__attribute__
关键字,它是是 GUN C
提供的,能编译器可以根据 __attribute__
的定义为函数,类型,对象根据约定的标志进行额外的操作,这样就可以在我们编写代码时对编译规则进行适当的修改. __attribute__ ((deprecated))
__attribute__((unavailable))
__attribute__((unused))
__attribute__((noreturn))
int x __attribute__ ((aligned (16))) = 0; //字节对齐,用更小的只来标示该类型,减少空间,最大不能超过链接器的最大对齐字节
struct student
{
char name[7];
uint32_t id;
char subject[5];
} __attribute__ ((aligned(4))); //总字节应为 12
short array[3] __attribute__ ((aligned (__BIGGEST_ALIGNMENT__))); //最大的机器字节码对齐
struct my_packed_struct
{
char c;
int i;
struct my_unpacked_struct s;
}__attribute__ ((__packed__)); //按类型具体大小对齐,char+int
__attribute__((format (printf, 1, 2))); //对可变参数的函数格式进行校验,(1表示为格式化字符串位置,2表示可变参位置)
attribute__((used))用于告诉编译器在目标文件中保留一个静态函数或者静态变量,即使它没有被引用。
__attribute__((always_inline)) 实现函数内联,对于简短频繁调用的函数,直接放入符号表,减少堆栈开销
__attribute__((constructor)) 修饰的函数在main函数之前执行,配合`__attribute__((destructor))`使用,常用统计库的时间
__attribute__((objc_designated_initializer)) 指定某个函数为初始化构造器,限制对象的创建接口,这点对于初始化封装内部必须实现的内部逻辑非常有
Objective-C runtime 理论上会在加载和初始化类的时候调用两个类方法: load and initialize。在讲解 method swizzling 的原文中 Mattt 老师指出出于安全性和一致性的考虑,方法交叉过程 永远 会在 load() 方法中进行。每一个类在加载时只会调用一次 load 方法。另一方面,一个 initialize 方法可以被一个类和它所有的子类调用,比如说 UIViewController 的该方法,如果那个类没有被传递信息,那么它的 initialize 方法就永远不会被调用了。
func load(){
let classCount = Int(objc_getClassList(nil, 0))
let types = UnsafeMutablePointer.allocate(capacity: classCount)
objc_getClassList(AutoreleasingUnsafeMutablePointer.init(types), Int32(classCount))
for index in 0..
元类: MetaClass, 元类是对类结构的描述,当前类的isA
指针指向它的元类,包含了当前类所需的最基本的数据结构及objc运行时的数据结构.类可以在此基础上上扩展自己的方式和属性. super指向它的父类
类: 具有相似功能和行为特点的事务的集合,对抽象事物的描述,就像元类是对类结构的描述一样
实例对象: 它是根据类的描述创建出来的,可以有多个
下面是网上关于它们之间的关系非常经典的一张图:
验证方式
//rootObject NSObject * 0x600003d307e0 0x0000600003d307e0
//isa Class NSObject 0x0000000103b56310 --> rootClass
NSObject* rootObject = [[NSObject alloc]init];
//rootClass NSObject * 0x103b56310 0x0000000103b56310
//isa Class 0x103b562c0 0x0000000103b562c0 --> rootMetaClass
NSObject* rootClass = [NSObject class];
//rootSuperClass NSObject * nil 0x0000000000000000
//isa Class 0x0
NSObject* rootSuperClass = [NSObject superclass];
// rootMetaClass Class 0x103b562c0 0x0000000103b562c0
Class rootMetaClass = object_getClass(rootClass);
// (NSObject *) $0 = 0x0000000103b562c0 = rootClass
NSObject* rootMetaSuperClass = [rootMetaClass superclass];
//superObject Person * 0x600003d2c290 0x0000600003d2c290
//isa Class Person 0x00000001035d0640 -> superClass
NSObject* superObject = [[Person alloc]init];
//superClass NSObject * 0x1035d0640 0x00000001035d0640
//isa Class 0x1035d0618 0x00000001035d0618 --> superSuperClass
NSObject* superClass = [Person class];
//superSuperClass NSObject * 0x103b56310 0x0000000103b56310
//isa Class 0x1035d0618 0x00000001035d0618
NSObject* superSuperClass = [Person superclass];
//(Class) superMetaClass = 0x00000001035d0618
Class superMetaClass = object_getClass(superClass);
//subObject Student * 0x600003d30810 0x0000600003d30810
//isa Class Student 0x00000001035d06e0 --> subClass
NSObject* subObject = [[Student alloc]init];
//subClass NSObject * 0x10a06e6e0 0x000000010a06e6e0
//isa Class 0x10a06e6b8 0x000000010a06e6b8 --> subSuperClass
NSObject* subClass = [Student class];
//subSuperClass NSObject * 0x1035d0640 0x00000001035d0640 -> subMetaClass
//isa Class 0x1035d0618 0x00000001035d0618
NSObject* subSuperClass = [Student superclass];
//(NSObject *) subSuperClass = 0x00000001035d0640
Class subMetaClass = object_getClass(subClass);
objc_object
,它定义了objc中类的最基本结构,内存管理,引用计数,isA指针处理,对象关联等/// 代表了一个class实例,objc_object是Runtime中的元类,objc中的id类型默认就是它
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY; //Class 为objc_class
bool hasNonpointerIsa();
bool isTaggedPointer();
bool isBasicTaggedPointer();
bool isExtTaggedPointer();
bool isClass();
// object may have associated objects?
bool hasAssociatedObjects();
void setHasAssociatedObjects();
// object may be weakly referenced?
bool isWeaklyReferenced();
void setWeaklyReferenced_nolock();
...
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
@interface NSObject {
Class isa;
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
bool isMetaClass() const { ...
return data()->flags & RW_META;
}
bool isRootClass() {
return getSuperclass() == nil;
}
bool isRootMetaclass() {
return ISA() == (Class)this; //object_class
}
};
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained _Nonnull id receiver;
__unsafe_unretained _Nonnull Class super_class;
};
因为NSObject
为OC中的基类,在NSObject.mm
中它的isA
是指向objc_class
,isA指针
isa_t::setClass(Class cls, objc_object *obj)
{
this->cls = cls;
}
inline void
objc_object::initClassIsa(Class cls)
{
initIsa(cls);
}
在objc收到dyld的loadImages
完成的回调后开始加载类,将类转换为Class类型,并添加到全局的类表中,
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 17.1
* frame #0: 0x000000010079995c libobjc.A.dylib`add_class_to_loadable_list(cls=NSObject) at objc-loadmethod.mm:66:20
frame #1: 0x00000001007a2040 libobjc.A.dylib`schedule_class_load(cls=NSObject) at objc-runtime-new.mm:3854:5
frame #2: 0x00000001007a2037 libobjc.A.dylib`schedule_class_load(cls=__NSStackBlock__) at objc-runtime-new.mm:3852:5
frame #3: 0x000000010079fda8 libobjc.A.dylib`prepare_load_methods(mhdr=0x00007ff818f25000) at objc-runtime-new.mm:3876:9
frame #4: 0x000000010079fb9a libobjc.A.dylib`load_images(path="/usr/lib/system/libsystem_blocks.dylib", mh=0x00007ff818f25000) at objc-runtime-new.mm:3198:9
frame #5: 0x0000000100023404 dyld`dyld4::RuntimeState::notifyObjCInit(dyld4::Loader const*) + 170
frame #6: 0x000000010003c525 dyld`dyld4::APIs::runAllInitializersForMain() + 181
frame #7: 0x000000010001a37d dyld`dyld4::prepare(dyld4::APIs&, dyld3::MachOAnalyzer const*) + 3443
frame #8: 0x00000001000194d4 dyld`start + 388
由 classref_t
转换为 object_class
类型,所以所有objc的的类最终都会使用object_class
结构体表示,class名字为object_class
中定义的name
void prepare_load_methods(const headerType *mhdr)
{
size_t count, i;
runtimeLock.assertLocked();
classref_t const *classlist =
_getObjc2NonlazyClassList(mhdr, &count);
for (i = 0; i < count; i++) {
schedule_class_load(remapClass(classlist[i]));
}