前言
之前,我们在探索动画及渲染相关原理的时候,我们输出了几篇文章,解答了
iOS动画是如何渲染,特效是如何工作的疑惑。我们深感系统设计者在创作这些系统框架的时候,是如此脑洞大开,也深深意识到了解一门技术的底层原理对于从事该方面工作的重要性。因此我们决定
进一步探究iOS底层原理的任务,本文探索的底层原理围绕“几种OC对象【实例对象、类对象、元类】、对象的isa指针、superclass、对象的方法调用、Class的底层本质”展开
一、概述
我们在上上篇探索OC的文章:Objective-C语言的的本质中,曾提到过 OC 是一种 面向对象 高级编程语言。因此本文紧接着上一篇文章的探索底层原理的步伐,开展 OC 面向对象 的语法,对 OC 的 几种 对象 类型 进行探究。
OC对象主要分为三种:
instance对象(实例对象)class对象(类对象)meta-class对象(元类对象) 并且,我们要探索 类对象(Class) 的补充和扩展的语法:Category(分类)
二、instance对象
通过OC语言编写的程序中,instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象。
在上篇文章OC对象的本质中,通过讨论 NSObject对象的底层实现、内存布局、通过继承关系了解 子类、孙子类的 内存布局...就是探索 instance对象 的底层过程的开端。现在简单回顾一下之前探索的结论,并继续探索!
在OC对象的本质这篇文章中,我们得到的结论:
- NSObject的底层实现:
目前官方暴露的头文件中的格式:
@interface NSObject <NSObject> {
Class isa ;
}
@end
简写格式:
@interface NSObject <NSObject> {
objc_class isa ;
}
@end
结构体objc_class的实现:
struct objc_class {
private:
isa_t isa;
public:
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
struct old_class_ext *ext;
....
}
- 创建一个NSObject对象
至少需要8个字节,在64位系统中,系统至少实际分配了16个字节。-
至少需要8个字节的内存(用于存放isa指针) -
实际分配了16个字节的内存(因为系统开发者在设计的时候,规定了内存分配的规则) -
OC对象的
内存对齐参数为 16- 若需要分配的内存不够16,则以给16个字节
- 若对象需要分配的内存超过16,则以能容纳对象的数据结构为前提,以最接近其的16最小公倍数为最终分配大小进行分配
-
一个OC对象在内存中的布局:
- 系统会在堆中开辟一块内存空间存放该对象
- 这块空间里还包含
成员变量和isa指针 - 然后栈里的 局部变量
指向这块存储空间的地址
-
系统 提供了
两个底层API给 开发者 进行 对内存分配情况的了解创建一个实例对象,至少需要多少内存? #import <objc/runtime.h> class_getInstanceSize([NSObject class]); 创建一个实例对象,实际上分配了多少内存? #import <malloc/malloc.h> malloc_size((__bridge const void *)obj);
-
所有的OC类的基类 都是 NSObject,而 instance对象本质上是通过 [[Class alloc]init]......出来的
alloc: 分配内存init: 初始化
int main(int argc, const char * argv[]) {
@autoreleasepool {
// obj1、obj2是NSObject的instance对象(实例对象)
NSObject *obj1 = [[NSObject alloc]init];
NSObject *obj2 = [[NSObject alloc]init];
// 通过打印可以看出,它们是不同的两个对象,分别占据着两块不同的内存
NSLog(@"obj1:%p",obj1);
NSLog(@"obj2:%p",obj2);
}
return 0;
}
我们随手写四个类:(继承关系如下:)
Car: NSObject- 增加了一个成员: int _year
CarRun: Car- 增加了一个方法:
- -(void)run;
- 增加了一个方法:
BBA_BMW:CarRun- 增加了一个成员: ;
- NSString*_nameplate;
- 增加了一个成员: ;
BBA_BMW_RunFaster:BBA_BMW- 增加了一个方法:
- -(void)runFaster;
- 增加了一个方法:
@interface Car :NSObject{
@public
int _year;
}
@end
@implementation Car
- (instancetype)init{
self = [super init];
if (self) {
_year = 1;
}
return self;
}
@end
@interface CarRun :Car
-(void)run;
@end
@implementation CarRun
-(void)run{
NSLog(@"%s",__func__);
}
@end
@interface BBA_BMW :CarRun{
@public
NSString*_nameplate;//汽车铭牌 8个字节
}
@end
@implementation BBA_BMW
@end
@interface BBA_BMW_RunFaster :BBA_BMW
-(void)runFaster;
@end
@implementation BBA_BMW_RunFaster
- (void)runFaster{
NSLog(@"%s",__func__);
}
@end
//main函数:
int main(int argc, const char * argv[]) {
@autoreleasepool {
// obj1、obj2是NSObject的instance对象(实例对象)
NSObject *obj1 = [[NSObject alloc]init];
NSObject *obj2 = [[NSObject alloc]init];
// 通过打印可以看出,它们是不同的两个对象,分别占据着两块不同的内存
NSLog(@"obj1:%p",obj1);
NSLog(@"obj2:%p",obj2);
//1.创建一个 基类 NSObject 的实例 分配 内存
NSLog(@"NSObject:getInstanceSize:%zd",class_getInstanceSize([NSObject class]));
NSLog(@"obj-malloc_size:%zd",malloc_size((__bridge const void*)obj1));
NSLog(@"=====================================================================");
//2.创建一个 基类 NSObject 的子类 Car 的实例 分配 内存
Car *car = [[Car alloc]init];
NSLog(@"Car-getInstanceSize:%zd",class_getInstanceSize([Car class]));
NSLog(@"Car-malloc_size:%zd",malloc_size((__bridge const void*)car));
NSLog(@"=====================================================================");
//3.创建一个 Car 的子类 CarRun 的实例 分配 内存
CarRun *car_run = [[CarRun alloc]init];
NSLog(@"CarRun-getInstanceSize:%zd",class_getInstanceSize([CarRun class]));
NSLog(@"CarRun-malloc_size:%zd",malloc_size((__bridge const void*)car_run));
NSLog(@"=====================================================================");
//4.创建一个 CarRun 的子类 BBA_BMW 的实例 分配 内存
BBA_BMW *bmw = [[BBA_BMW alloc]init];
NSLog(@"BBA_BMW-getInstanceSize:%zd",class_getInstanceSize([BBA_BMW class]));
NSLog(@"BBA_BMW-malloc_size:%zd",malloc_size((__bridge const void*)bmw));
NSLog(@"=====================================================================");
//5.创建一个 CarRun 的子类 BBA_BMW_RunFaster 的实例 分配 内存
BBA_BMW_RunFaster *bmw_runfaster = [[BBA_BMW_RunFaster alloc]init];
NSLog(@"BBA_BMW_RunFaster-getInstanceSize:%zd",class_getInstanceSize([BBA_BMW_RunFaster class]));
NSLog(@"BBA_BMW_RunFaster-malloc_size:%zd",malloc_size((__bridge const void*)bmw_runfaster));
}
return 0;
}
通过打印结果,结合类的声明 我们不得出结论:
添加方法,不影响Instance对象的内存分配: 初始化一个实例对象,至少所需要的内存添加成员对象,影响Instance对象的内存分配:初始化一个实例对象,至少所需要的内存Instance 对象的实际所需内存与其自身的成员对象有关,也即是:instance对象在内存中存储的信息与其自身的成员对象有关,成员对象:- isa指针(基类NSObject对象内部存在)
- 其他成员变量的具体值
三、Class对象
1.每个类 在内存中 有且只有一个 Class对象
我们在探索NSObject对象的时候,看到了底层代码:
struct objc_class {
private:
isa_t isa;
public:
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
struct old_class_ext *ext;
....
}
结合 NSObject.h文件:
@interface NSObject <NSObject> {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-interface-ivars"
Class isa OBJC_ISA_AVAILABILITY;
#pragma clang diagnostic pop
...
}
从前面两段代码,我们不难得知基类NSObject中有一个 Class类型的对象:isa指针
我们把前面创建了四个类,且有继承关系的代码,通过clang指令转换成c++:
结合前面的继承关系,和转换成c++底层代码之后的具体情况(如上图),我们不难得出一个结论:
每个类在内存中有且只有一个class对象- 还可以通过打印内存地址证明:
Class objectClass1 = [object1 class]; Class objectClass2 = [object2 class]; Class objectClass3 = object_getClass(object1); Class objectClass4 = object_getClass(object2); Class objectClass5 = [NSObject class]; // 通过打印可以看出,上面几个方法返回的都是同一个类对象,内存地址都一样 NSLog(@"class - %p %p %p %p %p %d", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5);- 我们可以通过NSObject的
类方法、实例方法获得类对象:+ (Class)class- (Class)class
- 我们还可以通过 Runtime的API获得
类对象:-
Class _Nullable object_getClass(id _Nullable obj) - 该API效力等同于
+ (Class)class、- (Class)class
-
注意: class方法返回的一直是类对象,所以哪怕这样写还是会返回类对象
Class objectMetaClass2 = [[[NSObject class] class] class];
2.Class对象在内存中存储的信息
我们查看苹果官方开源的runtime代码我们得知(代码获取以及相关探索过程参考这篇文章:OC的本质),Class底层代码如下:
struct objc_class {
private:
isa_t isa;
public:
Class superclass;
const char *name;
uint32_t version;
uint32_t info;
uint32_t instance_size;
struct old_ivar_list *ivars;
struct old_method_list **methodLists;
Cache cache;
struct old_protocol_list *protocols;
// CLS_EXT only
const uint8_t *ivar_layout;
//struct old_class_ext *ext;
uint32_t size;
const uint8_t *weak_ivar_layout;
struct old_property_list **propertyLists;
....
}
查看源码之后,我们不难得出结论,class对象在内存中存储的信息:
-
isa指针
-
superclass指针
-
- 类的属性信息(
@property)、类的对象方法信息(instance method)
- 我们通过
runtimeAPI遍历一下methodLists内部的信息,得知:struct old_method_list **methodLists内部存储的都是 类的对象方法信息(instance method)
- 类的属性信息(
-
- 类的协议信息(
protocol)、类的成员变量信息(ivar)
- 类的协议信息(
- ....
3.总结
对instance对象和Class对象的总结:
isa指针:每个类 在内存中 有且只有一个 Class对象,isa指针class对象在内存中存储的信息:-
isa指针
-
superclass指针
-
- 类的属性信息(
@property)、类的对象方法信息(instance method)
- 类的属性信息(
-
- 类的协议信息(
protocol)、类的成员变量信息(ivar)
- 类的协议信息(
- ....
-
成员变量的值 存储在实例对象中: 是存储在实例对象中的,因为只有当我们创建实例对象的时候才为成员变赋值成员对象的类型、值、名称,存储在在class对象中: 但是 成员变量叫什么名字,是什么类型,只需要有一份就可以了。所以存储在class对象中
三、meta-class对象
1.meta-class 相关的函数
我们在阅读苹果官方开源的objc代码时,我们发现有若干个与class相关的内部实现函数:
我们前面已经探究了比较常见的
instance-对象、class对象、基类NSObject、superclass....
但我们在翻阅源码的时候发现了一个meta-class。
现在我们去了解一下meta-class
我们全局搜索metaclass,找到一个返回Class对象的objc_getMetaClass函数的实现:
我们继续跳进去看一下
ISA()函数的实现:
我们从这段代码可以看到isa对象调用了一个函数,而isa指针,本身就存在于 class内部:
isa内部定义为:
函数
inline Class isa_t::getDecodedClass(bool authenticated)
的内部实现:
2.meta-class相关的探索结论
综上,我们不难得出结论:
- 我们可以通过 当前class的isa指针 拿到
metaClass - 若为当前Class是metaClass: 则 返回 其自身
- metaClass 的 metaClass 就是其 自身:
- 我们结合Class的内部结构体实现,不难得出结论:
- metaClass 也是 class 类型(meta-class对象和class对象的内存结构是一样的)
- metaClass 是一种特殊 的 class 类型(与其它Class对象可能有不一样的用途)
- meta-class 存储的方法列表为
类的类方法信息(class method) meta-class的 superclass为NSObject类型的 meta-classmeta-class的 superclass的 superclass 为 基类NSObject(非元类)meta-class的 superclass的 superclass 的 superclass,也即是 基类NSObject(非元类)的 superclass的 为 nil- 基类
NSObject(非元类)的superclass 为 nil - 补充,获取meta-class的函数 除了 函数
objc_getMetaClass, 还有object_getClass
3.meta-class简要总结
meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括:
- isa指针
- superclass指针
- 类的类方法信息(class method)
- ....
四、isa和superclass
1.isa指针
通过前面篇幅的介绍,我们得知:每个类的实例对象、类对象、元类对象都有一个isa指针
void test11(void){
Class animalMetaCls = getMetaClassFromClass([BBA_BMW_RunFaster class]);
NSLog(@"superclass:%@",[BBA_BMW_RunFaster superclass]);
NSLog(@"superclass-superclass:%@",[[BBA_BMW_RunFaster superclass] superclass]);
NSLog(@"superclass-superclass-superclass:%@",[[[BBA_BMW_RunFaster superclass] superclass] superclass]);
NSLog(@"superclass-superclass-superclass-superclass:%@",[[[[BBA_BMW_RunFaster superclass] superclass] superclass] superclass]);
NSLog(@"superclass-superclass-superclass-superclass-superclass:%@",[[[[[BBA_BMW_RunFaster superclass] superclass] superclass] superclass] superclass]);
NSLog(@"====================");
NSLog(@"metaClass:%@",animalMetaCls);
NSLog(@"metaClass-superclass:%@======metaClass-superclass_class_isMetaClass:%d",[animalMetaCls superclass],class_isMetaClass([animalMetaCls superclass]));
NSLog(@"metaClass-superclass-superclass:%@======metaClass-superclass-superclass_class_isMetaClass:%d",[[animalMetaCls superclass]superclass],class_isMetaClass([[animalMetaCls superclass]superclass]));
NSLog(@"====================");
NSLog(@"metaClass-superclass-superclass-superclass:%@======metaClass-superclass-superclass-superclass_class_isMetaClass:%d",[[[animalMetaCls superclass]superclass]superclass],class_isMetaClass([[[animalMetaCls superclass]superclass]superclass]));
NSLog(@"metaClass-superclass-superclass-superclass-superclass:%@======metaClass-superclass-superclasss-superclass-superclass_class_isMetaClass:%d",[[[[animalMetaCls superclass]superclass]superclass]superclass],class_isMetaClass([[[[animalMetaCls superclass]superclass]superclass]superclass]));
NSLog(@"metaClass-superclass-superclass-superclass-superclass-superclass:%@======metaClass-superclass-superclass-superclasss-superclass-superclass_class_isMetaClass:%d",[[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass],class_isMetaClass([[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass]));
NSLog(@"metaClass-superclass-superclass-superclass-superclass-superclass-superclass:%@======metaClass-superclass-superclass--superclass-superclasss-superclass-superclass_class_isMetaClass:%d",[[[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass] superclass],class_isMetaClass([[[[[[animalMetaCls superclass]superclass]superclass]superclass] superclass] superclass]));
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test11();
}
return 0;
}
- instance的isa指向class
- 当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用
- class的isa指向meta-class
- 当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用
- meta-class的isa指向基类的meta-class
- 基类的isa指向自己
2.superclass指针
通过前面篇幅的介绍,我们得知:每个类的类对象、元类对象都有一个superclass指针
- class的superclass指针指向父类的class
- 如果没有父类,superclass指针为nil
- meta-class的superclass指向父类的meta-class
- 基类的meta-class的superclass指向基类的class
3.方法的调用|寻址
instance调用对象方法的轨迹
- isa找到class,方法不存在,就通过superclass找父类 class调用类方法的轨迹
- isa找meta-class,方法不存在,就通过superclass找父类
4.证明isa指针的指向如上结论?
我们通过如下代码证明:
NSObject *object = [[NSObject alloc] init];
Class objectClass = [NSObject class];
Class objectMetaClass = object_getClass([NSObject class]);
NSLog(@"%p %p %p", object, objectClass, objectMetaClass);
打断点并通过控制台打印相应对象的isa指针
我们发现object->isa与objectClass的地址不同,这是因为从64bit开始,isa需要进行一次位运算,才能计算出真实地址。而位运算的值我们可以通过下载objc源代码找到。
我们通过位运算进行验证。
我们发现,object-isa指针地址0x001dffff96537141经过同0x00007ffffffffff8位运算,得出objectClass的地址0x00007fff96537140
接着我们来验证class对象的isa指针是否同样需要位运算计算出meta-class对象的地址。 当我们以同样的方式打印objectClass->isa指针时,发现无法打印
同时也发现左边objectClass对象中并没有isa指针。我们来到Class内部看一下
typedef struct objc_class *Class;
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
相信了解过isa指针的同学对objc_class结构体内的内容很熟悉了,今天这里不深入研究,我们只看第一个对象是一个isa指针,为了拿到isa指针的地址,我们自己创建一个同样的结构体并通过强制转化拿到isa指针。
struct hp_objc_class{
Class isa;
};
Class objectClass = [NSObject class];
struct hp_objc_class *objectClass2 = (__bridge struct hp_objc_class *)(objectClass);
此时我们重新验证一下
确实,objectClass2的isa指针经过位运算之后的地址是meta-class的地址。
五、进一步探索 Class对象的底层结构
1.Class的本质
我们知道不管是类对象还是元类对象,类型都是Class,class和mete-class的底层都是objc_class结构体的指针,内存中就是结构体,本章来探寻Class的本质。
Class objectClass = [NSObject class];
Class objectMetaClass = object_getClass([NSObject class]);
点击Class来到内部,我们可以发现
typedef struct objc_class *Class;
Class对象其实是一个指向objc_class结构体的指针。因此我们可以说类对象或元类对象在内存中其实就是objc_class结构体。
我们来到objc_class内部,可以看到这段在底层原理中经常出现的代码。
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
这部分代码相信在文章中很常见,但是OBJC2_UNAVAILABLE;说明这些代码已经不在使用了。那么目前objc_class的结构是什么样的呢?我们通过objc源码中去查找objc_class结构体的内容。
我们发现这个结构体继承 objc_object 并且结构体内有一些函数,因为这是c++结构体,在c上做了扩展,因此结构体中可以包含函数。我们来到objc_object内,截取部分代码
我们发现objc_object中有一个isa指针,那么objc_class继承objc_object,也就同样拥有一个isa指针
那么我们之前了解到的,类中存储的类的成员变量信息,实例方法,属性名等这些信息在哪里呢。我们来到class_rw_t中,截取部分代码,我们发现class_rw_t中存储着方法列表,属性列表,协议列表等内容。
而class_rw_t是通过bits调用data方法得来的,我们来到data方法内部实现。我们可以看到,data函数内部仅仅对bits进行&FAST_DATA_MASK操作
而成员变量信息则是存储在class_ro_t内部中的,我们来到class_ro_t内查看。
最后总结通过一张图进行总结
2.证明上述通过阅读源码得出的结论正确与否。
因为底层的部分API无法直接在项目中使用,且我们通过阅读了苹果官方开源的底层代码,所以我们可以模仿官方开源的底层实现,在自己的工程 自定义结构体模仿Class的底层实现。
如果我们自己写的结构和objc_class真实结构是一样的,那么当我们强制转化的时候,就会一一对应的赋值。那么我们就可以拿到结构体内部的信息。
2.1 自定义结构体,模仿苹果官方class底层实现
下列代码是我们仿照objc_class结构体,提取其中需要使用到的信息,自定义的一个结构体: (注意,要把.m文件改成.mm,因为这段代码的实现属于c++的语法)
#import <Foundation/Foundation.h>
#ifndef HPClassInfo_h
#define HPClassInfo_h
# if __arm64__
# define ISA_MASK 0x0000000ffffffff8ULL
# elif __x86_64__
# define ISA_MASK 0x00007ffffffffff8ULL
# endif
#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;
struct bucket_t {
cache_key_t _key;
IMP _imp;
};
struct cache_t {
bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
};
struct entsize_list_tt {
uint32_t entsizeAndFlags;
uint32_t count;
};
struct method_t {
SEL name;
const char *types;
IMP imp;
};
struct method_list_t : entsize_list_tt {
method_t first;
};
struct ivar_t {
int32_t *offset;
const char *name;
const char *type;
uint32_t alignment_raw;
uint32_t size;
};
struct ivar_list_t : entsize_list_tt {
ivar_t first;
};
struct property_t {
const char *name;
const char *attributes;
};
struct property_list_t : entsize_list_tt {
property_t first;
};
struct chained_property_list {
chained_property_list *next;
uint32_t count;
property_t list[0];
};
typedef uintptr_t protocol_ref_t;
struct protocol_list_t {
uintptr_t count;
protocol_ref_t list[0];
};
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize; // instance对象占用的内存空间
#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;
};
struct class_rw_t {
uint32_t flags;
uint32_t version;
const class_ro_t *ro;
method_list_t * methods; // 方法列表
property_list_t *properties; // 属性列表
const protocol_list_t * protocols; // 协议列表
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
};
#define FAST_DATA_MASK 0x00007ffffffffff8UL
struct class_data_bits_t {
uintptr_t bits;
public:
class_rw_t* data() { // 提供data()方法进行 & FAST_DATA_MASK 操作
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
};
/* OC对象 */
struct hp_objc_object {
void *isa;
};
/* 类对象 */
struct hp_objc_class : hp_objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
public:
class_rw_t* data() {
return bits.data();
}
hp_objc_class* metaClass() { // 提供metaClass函数,获取元类对象
// 前面我们讲解过,isa指针需要经过一次 & ISA_MASK操作之后才得到真正的地址
return (hp_objc_class *)((long long)isa & ISA_MASK);
}
};
#endif /* HPClassInfo_h */
2.2 添加几个类,用于制造验证数据
接下来我们将自己定义的类强制转化为我们自定义的精简的class结构体类型。
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "HPClassInfo.h"
/* Person */
@interface Person : NSObject <NSCopying>
{
@public
int _age;
}
@property (nonatomic, assign) int height;
- (void)personMethod;
+ (void)personClassMethod;
@end
@implementation Person
- (void)personMethod {}
+ (void)personClassMethod {}
@end
/* Student */
@interface Student : Person <NSCoding>
{
@public
int _no;
}
@property (nonatomic, assign) int score;
- (void)studentMethod;
+ (void)studentClassMethod;
@end
@implementation Student
- (void)studentMethod {}
+ (void)studentClassMethod {}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *object = [[NSObject alloc] init];
Person *person = [[Person alloc] init];
Student *student = [[Student alloc] init];
hp_objc_class *objectClass = (__bridge hp_objc_class *)[object class];
hp_objc_class *personClass = (__bridge hp_objc_class *)[person class];
hp_objc_class *studentClass = (__bridge hp_objc_class *)[student class];
hp_objc_class *objectMetaClass = objectClass->metaClass();
hp_objc_class *personMetaClass = personClass->metaClass();
hp_objc_class *studentMetaClass = studentClass->metaClass();
class_rw_t *objectClassData = objectClass->data();
class_rw_t *personClassData = personClass->data();
class_rw_t *studentClassData = studentClass->data();
class_rw_t *objectMetaClassData = objectMetaClass->data();
class_rw_t *personMetaClassData = personMetaClass->data();
class_rw_t *studentMetaClassData = studentMetaClass->data();
// 0x00007ffffffffff8
NSLog(@"%p %p %p %p %p %p", objectClassData, personClassData, studentClassData,
objectMetaClassData, personMetaClassData, studentMetaClassData);
return 0;
}
2.3 打断点求证
通过打断点,我们可以看到class内部信息。
至此,我们再次拿出那张经典的图,挨个分析图中isa指针和superclass指针的指向
2.4 instance对象
首先我们来看instance对象,我们通过前面篇幅的介绍不难得知:
instance对象中存储着isa指针和其他成员变量,instance对象的isa指针是指向其类对象地址的 我们首先分析上述代码中我们创建的:object,person,student三个instance对象- 与其相对应的类对象
objectClass,personClass,studentClass。
从上图中我们可以发现:
-
instance对象中确实存储了isa指针和其成员变量
-
- 将
instance对象的isa指针经过&运算之后计算出的地址确实是其相应类对象的内存地址
- 将
-
- 由此我们证明isa,superclass指向图中的1,2,3号线。
2.5 class对象
接着我们来看class对象,同样通过前面的篇幅介绍,我们明确知道:
class对象中存储着:isa指针,superclass指针类的属性信息,类的成员变量信息,类的对象方法,和类的协议信息
- 而通过上面对object源码的分析,我们知道 以上 信息存储在
class对象的class_rw_t中我们通过强制转化来窥探其中的内容。如下图
- 上图中我们通过模拟对person类对象调用.data函数:
-
- 即对bits进行&FAST_DATA_MASK(0x00007ffffffffff8UL)运算
-
- 并转化为class_rw_t。即上图中的personClassData。
-
- 其中我们发现
成员变量信息,对象方法,属性等信息只显示first第一个- 如果想要拿到更多的需要通过代码将指针后移获取。
- 上图中的instaceSize = 16 也同 person对象中isa指针8个字节+_age4个字节+_height4个字节相对应起来。(这里不在展开对objectClassData及studentClassData进行分析,基本内容同personClassData相同。)
那么类对象中的isa指针和superclass指针的指向是否如那张经典的图示呢?我们来验证一下。
通过上图中的内存地址的分析,由此我们证明:
isa,superclass指向图中,isa指针的4,5,6号线以及superclass指针的10,11,12号线
2.6 meta-class对象
最后我们来看 meta-class元类对象
前面篇幅中提到 meta-class中存储着:
isa指针superclass指针类的类方法信息。- 同时我们知道
meta-class元类对象与class类对象,具有相同的结构,只不过存储的信息不同。 - 并且
元类对象的isa指针指向基类的元类对象, 基类的元类对象的isa指针指向自己元类对象的superclass指针指向其父类的元类对象基类的元类对象的superclass指针指向其类对象。
与class对象相同,我们同样通过模拟对person元类对象调用.data函数,即对bits进行&FAST_DATA_MASK(0x00007ffffffffff8UL)运算,并转化为class_rw_t。
首先我们可以看到结构同personClassData相同,并且成员变量及属性列表等信息为空,而methods中存储着类方法personClassMethod。
接着来验证isa及superclass指针的指向是否同上图序号标注一样。
上图中通过地址证明meta-class的isa指向基类的meta-class,基类的isa指针也指向自己。
上图中通过地址证明meta-class的superclass指向父类的meta-class,基类的meta-class的superclass指向基类的class类。
总结
本文整个篇幅 介绍了 OC面向对象语法中的 几种对象(【实例对象、类对象、元类】)、对象的isa指针、superclass、对象的方法调用、以及Class的底层本质且验证了阅读源码的理解推断。本文只对课题相关的底层代码进行阅读或验证,并没有铺展开来介绍。本文就此收尾,其它底层原理相关的要点,待下回分晓
专题系列文章
1.前知识
- 01-探究iOS底层原理|综述
- 02-探究iOS底层原理|编译器LLVM项目【Clang、SwiftC、优化器、LLVM】
- 03-探究iOS底层原理|LLDB
- 04-探究iOS底层原理|ARM64汇编
2. 基于OC语言探索iOS底层原理
- 05-探究iOS底层原理|OC的本质
- 06-探究iOS底层原理|OC对象的本质
- 07-探究iOS底层原理|几种OC对象【实例对象、类对象、元类】、对象的isa指针、superclass、对象的方法调用、Class的底层本质
- 08-探究iOS底层原理|Category底层结构、App启动时Class与Category装载过程、load 和 initialize 执行、关联对象
- 09-探究iOS底层原理|KVO
- 10-探究iOS底层原理|KVC
- 11-探究iOS底层原理|探索Block的本质|【Block的数据类型(本质)与内存布局、变量捕获、Block的种类、内存管理、Block的修饰符、循环引用】
- 12-探究iOS底层原理|Runtime1【isa详解、class的结构、方法缓存cache_t】
- 13-探究iOS底层原理|Runtime2【消息处理(发送、转发)&&动态方法解析、super的本质】
- 14-探究iOS底层原理|Runtime3【Runtime的相关应用】
- 15-探究iOS底层原理|RunLoop【两种RunloopMode、RunLoopMode中的Source0、Source1、Timer、Observer】
- 16-探究iOS底层原理|RunLoop的应用
- 17-探究iOS底层原理|多线程技术的底层原理【GCD源码分析1:主队列、串行队列&&并行队列、全局并发队列】
- 18-探究iOS底层原理|多线程技术【GCD源码分析1:dispatch_get_global_queue与dispatch_(a)sync、单例、线程死锁】
- 19-探究iOS底层原理|多线程技术【GCD源码分析2:栅栏函数dispatch_barrier_(a)sync、信号量dispatch_semaphore】
- 20-探究iOS底层原理|多线程技术【GCD源码分析3:线程调度组dispatch_group、事件源dispatch Source】
- 21-探究iOS底层原理|多线程技术【线程锁:自旋锁、互斥锁、递归锁】
- 22-探究iOS底层原理|多线程技术【原子锁atomic、gcd Timer、NSTimer、CADisplayLink】
- 23-探究iOS底层原理|内存管理【Mach-O文件、Tagged Pointer、对象的内存管理、copy、引用计数、weak指针、autorelease
3. 基于Swift语言探索iOS底层原理
关于函数、枚举、可选项、结构体、类、闭包、属性、方法、swift多态原理、String、Array、Dictionary、引用计数、MetaData等Swift基本语法和相关的底层原理文章有如下几篇:
其它底层原理专题
1.底层原理相关专题
2.iOS相关专题
- 01-iOS底层原理|iOS的各个渲染框架以及iOS图层渲染原理
- 02-iOS底层原理|iOS动画渲染原理
- 03-iOS底层原理|iOS OffScreen Rendering 离屏渲染原理
- 04-iOS底层原理|因CPU、GPU资源消耗导致卡顿的原因和解决方案