
开局一张图,请看下面大家都熟悉的苹果官方给出的类的ISA走向
和类继承链
的图解(我在原图上加了几个对象、类、元类的名称分别用不同颜色标注):
isa分析拓展到类和元类
上图:

我们根据上图可以看到有两条线
,一条虚线
是isa
的走向路线,一条实线
是类继承链
的走向。我们接下来就来验证下这张图片的两条线的走向结果。
ps:补充说明:下面的验证过程会用到前面文章讲到的掩码 ISA_MASK
,在我当前的开发环境下从objc(objc4-818.2)
源码拿到的ISA_MASK __x86_64__
架构下:0x00007ffffffffff8ULL
,__arm64__
架构下:0x0000000ffffffff8ULL
,使用的时候去掉后面的ULL
,ULL
只是表示这个是无符号长整型(unsigned long long)
这里不再做过多赘述,不懂得看前面的文章 初探对象原理(三)
1,isa
走向路线(虚线)
选取Person 对象
这条线进行分析,其实选ioser 对象
也是一样的。
(1)根据图文走向分析第一点:对象(person)
的isa
指向类(ZYPerson)
,我们利用lldb
来打印(打断点在NSLog
处)验证:
直接上代码:
#import <Foundation/Foundation.h>
#import "ZYIoser.h"
#import "ZYPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//掩码(x86_64):0x00007ffffffffff8
ZYPerson *person = [ZYPerson alloc];
NSLog(@"person====%@====%p",person,person);
}
return 0;
}
(lldb) p/x person
(ZYPerson *) $0 = 0x0000000100505640
(lldb) x/4gx 0x0000000100505640
0x100505640: 0x011d800100008345 0x0000000000000000
0x100505650: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x011d800100008345 & 0x00007ffffffffff8
(long) $1 = 0x0000000100008340
(lldb) po 0x0000000100008340
ZYPerson
(lldb)
加上图解:

代码解释:
利用p/x
perspn
得出指针地址0x0000000100505640
做格式化操作 x/4gx
取出person
的 isa
指针地址0x011d800100008345
,利用这个isa
0x011d800100008345
和ISA_MASK (0x00007ffffffffff8)
做 与&
操作得出当前 person
的类 (class)
的地址0x0000000100008340
,在po
打印该地址验证得出ZYPerson
。
结论:
对象(person)
的isa
指向他的类(ZYPerson)
(2)根据图文走向分析第二点:类(ZYPerson)
的isa
指向元类(ZYPerson_meta)
,我们利用lldb
来打印验证:
直接上代码:
(lldb) p/x person
(ZYPerson *) $0 = 0x0000000108104d10
(lldb) x/4gx 0x0000000108104d10
0x108104d10: 0x011d800100008345 0x0000000000000000
0x108104d20: 0x0000000000000000 0x0000000000000000
(lldb) p/x 0x011d800100008345 & 0x00007ffffffffff8
(long) $1 = 0x0000000100008340
(lldb) po 0x0000000100008340
ZYPerson
(lldb) x/4gx 0x0000000100008340
0x100008340: 0x0000000100008318 0x00007fff88962008
0x100008350: 0x00007fff201e9af0 0x0000802c00000000
(lldb) p/x 0x0000000100008318 & 0x00007ffffffffff8
(long) $3 = 0x0000000100008318
(lldb) po 0x0000000100008318
ZYPerson
(lldb)
加上图解:

代码解释:
利用x/4gx
对ZYPerson
指针地址0x0000000100008340
做格式化操作 取出ZYPerson
的 isa
指针地址0x0000000100008318
,利用这个isa
指针地址和ISA_MASK (0x00007ffffffffff8)
做 与&
操作得出一个地址0x0000000100008318
,po
打印该地址得出同样的ZYPerson
。可以猜测这个和类地址0x0000000100008340
不一样的地址0x0000000100008318
就是我们常说的元类
的地址。
我们还可以从另一方面查看这个特殊的地址到底是个啥,我们利用烂苹果(MachOView)
来查看下Mach-O
文件,我们把项目的Mach-O
文件拉到MachOView
打开,找到下面的符号列表,往下翻我们可以看到关于ZYPerson 类
的部分 有一个奇怪的名字OBJC_METAcALSS_$ZYPeson
从字面的意思我们也可以猜测这就是我们常说的元类
并且他不是我们创建的而且跟ZYPerson绑定,证明是系统自己帮我们创建的。

结论:
类(ZYPerson)
的isa
指向他的元类(ZYPerson_meta)
(3)根据图文走向分析第三点:元类(ZYPerson_meta)
的isa
指向根元类(NSObject_meta)
,我们利用lldb
来打印验证:
直接上代码:
(lldb) x/4gx 0x0000000100008318
0x100008318: 0x00007fff88961fe0 0x00007fff88961fe0
0x100008328: 0x0000000100704e00 0x0002e03500000003
(lldb) p/x 0x00007fff88961fe0 & 0x00007ffffffffff8
(long) $5 = 0x00007fff88961fe0
(lldb) po 0x00007fff88961fe0
NSObject
(lldb)
加上图解:

代码解释:
利用x/4gx
对ZYPerson_meta
指针地址0x0000000100008318
做格式化操作 取出ZYPerson_meta
的 isa
指针地址0x00007fff88961fe0
,利用这个isa
指针地址和ISA_MASK (0x00007ffffffffff8)
做 与&
操作得出一个地址0x00007fff88961fe0
,po
打印该地址得出同样的NSObject
。证明元类 ZYPerson_meta
的isa
指向 根元类 NSObject_meta
。
结论:
元类(ZYPerson_meta)
的isa
指向他的根元类(NSObject_meta)
(4)根据图文走向分析第四点:根元类(NSObject_meta)
的isa
指向自己(NSObject_meta)
,我们利用lldb
来打印验证:
直接上代码:
(lldb) p/x NSObject.class
(Class) $8 = 0x00007fff88962008 NSObject
(lldb) x/4gx 0x00007fff88962008 & 0x00007ffffffffff8
error: memory read takes a start address expression with an optional end address expression.
Expressions should be quoted if they contain spaces or other special characters.
(lldb) x/4gx 0x00007fff88962008
0x7fff88962008: 0x00007fff88961fe0 0x0000000000000000
0x7fff88962018: 0x000000010855c3b0 0x0001801000000003
(lldb) p/x 0x00007fff88961fe0 & 0x00007ffffffffff8
(long) $9 = 0x00007fff88961fe0
(lldb) po 0x00007fff88961fe0
NSObject
(lldb)
加上图解:

代码解释:
利用p/x NSObject.class
对NSObject_meta
打印得到地址0x00007fff88962008
,做格式化操作取出NSObject_meta
的 isa
指针地址0x00007fff88961fe0
,利用这个isa
指针地址和ISA_MASK (0x00007ffffffffff8)
做 与&
操作得出一个地址0x00007fff88961fe0
,po
打印该地址得出同样的NSObject
。证明根元类 NSObject_meta
的isa
指向 根元类 NSObject_meta
自己。
结论:
根元类(NSObject_meta)
的isa
指向自己(NSObject_meta)
2,类继承链
的走向(实线)
根据上面isa
走向的分析我们下面可以用同样的方法来验证类继承链
的走向。这里我们可以更简单的利用OC
本身自带的API
来打印 出对应的信息。如下:
#pragma mark - NSObject 元类链
void ZYTestNSObject(void){
/*
* NSObject 继承链 分析 打印
**/
// NSObject实例对象
NSObject *object1 = [NSObject alloc];
// NSObject类
Class class = object_getClass(object1);
// NSObject元类
Class metaClass = object_getClass(class);
// NSObject根元类
Class rootMetaClass = object_getClass(metaClass);
// NSObject根根元类
Class rootRootMetaClass = object_getClass(rootMetaClass);
NSLog(@"\nNSObject 继承链 分析 打印 :\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类\n",object1,class,metaClass,rootMetaClass,rootRootMetaClass);
/*
* ZYPerson 继承链 分析 打印
**/
// ZYPerson元类
//获取ZYPerson 的元类pMetaClass
Class pMetaClass = object_getClass(ZYPerson.class);
//根据获取ZYPerson 的元类pMetaClass获取其 父类 psuperClass
Class psuperClass = class_getSuperclass(pMetaClass);
NSLog(@"\nZYPerson 继承链 分析 打印 :\n%@ - %p\n",psuperClass,psuperClass);
/*
* ZYIoser 继承链 分析 打印
**/
// ZYIoser -> ZYPerson -> NSObject
// 元类也有一条继承链
//获取ZYIoser 的元类iMetaClass
Class iMetaClass = object_getClass(ZYIoser.class);
//根据获取ZYIoser 的元类iMetaClass获取其 父类 isuperClass
Class isuperClass = class_getSuperclass(iMetaClass);
NSLog(@"\nZYIoser 继承链 分析 打印 :\n%@ - %p\n",isuperClass,isuperClass);
/*
* NSObject 继承链 验证 打印
**/
// NSObject 根类特殊情况 获取 NSObject 的父类
Class nsuperClass = class_getSuperclass(NSObject.class);
NSLog(@"\n获取 NSObject 的父类 打印 :\n%@ - %p\n",nsuperClass,nsuperClass);
// 根元类 -> NSObject 根据上面获取到NSObject 的元类 去获取其根源类
Class rnsuperClass = class_getSuperclass(metaClass);
NSLog(@"\n获取NSObject元类的根源类 打印 :\n%@ - %p\n",rnsuperClass,rnsuperClass);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
//掩码 0x00007ffffffffff8
// ZYPerson *person = [ZYPerson alloc];
// 继承链
ZYTestNSObject();
}
return 0;
}
2021-06-22 10:08:32.579807+0800 ZYProjectFour001[90252:3084099]
NSObject 继承链 分析 打印 :
0x105b3e900 实例对象
0x7fff808db388 类
0x7fff808db360 元类
0x7fff808db360 根元类
0x7fff808db360 根根元类
2021-06-22 10:08:32.580551+0800 ZYProjectFour001[90252:3084099]
ZYPerson 继承链 分析 打印 :
NSObject - 0x7fff808db360
2021-06-22 10:08:32.580677+0800 ZYProjectFour001[90252:3084099]
ZYIoser 继承链 分析 打印 :
ZYPerson - 0x100008360
2021-06-22 10:08:32.580735+0800 ZYProjectFour001[90252:3084099]
获取 NSObject 的父类 打印 :
(null) - 0x0
2021-06-22 10:08:32.580818+0800 ZYProjectFour001[90252:3084099]
获取NSObject元类的根源类 打印 :
NSObject - 0x7fff808db388
Program ended with exit code: 0
从上面我们可以看到:
1,NSObject类
的元类
、根元类
、根根元类
都是同一个地址0x7fff808db360
,所以我们可以验证NSObject
的元类NSObject_meta
的根元类
是自己 NSObject_meta
.
2,从ZYPerson的继承链打印
可以验证ZYPerson_meta
的根源类
是NSObject_meta
。
3,根据ZYIoser继承链分析打印
可以验证,继承与ZYPerson类
的ZYIoser类
的元类ZYIoser_meta
的根元类
是ZYPerson_meta
.
4,根据NSObject继承链
验证打印可以验证:
(1)NSObject类
的父类为null
(2)并且看到获取到的NSObject
的元类(NSObject_meta)
的根源类
还是NSObject类
自己因为打印的地址为:0x7fff808db388
和 第一次打印的NSObject类
的地址0x7fff808db388
一样。
类的结构初探:
根据上面对类的继承、isa的走向
分析,我们现在进一步探究,类它里面到底有什么:我们先直接利用lldb
来打印一下类
:

根据上面的打印进一步探究分析类的结构,那我们就回到我们的objc
源码,我们通过前面对对象的原理分析可以知道在底层对于类Class
也有对应的objc_class
,那么我们就在全局搜索里去搜 objc_class
的实现,我们可以发现有两个,但是仔细查看可以发现其中一个是在object2
被废弃了。所以我们直接看另一个,代码如下:(代码太长我省略了部分方法
和静态变量
不占用类内存的代码,我们要分析的是类的内存里面到底有什么)
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// 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
/*
*此处省略了部分代码
*/
class_rw_t *data() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
/*
*此处省略了大量不占用类内存的方法以及其他代码
*/
};
经过简单分析我们得出的代码可以发现 占用内存的只有四个(必备的isa
、superclass
、cache
、bits
)。里面的isa
、superclass
我们都熟悉,但是cache
、bits
我们不知道是什么,但是我们在clang
的时候我们会发现methodslist
、ivarlist
、propety
、等东西都存在bit
里并且我们在代码的时候发现在注释和下方代码里有很多东西都是从bit
里获取的比如class_rw_t
,我们看看class_rw_t
:
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
/*
* ***********************删除了部分代码***********************
*/
public:
void setFlags(uint32_t set)
{
__c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
}
void clearFlags(uint32_t clear)
{
__c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
ASSERT((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
class_rw_ext_t *ext() const {
return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
}
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
} else {
return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
}
}
class_rw_ext_t *deepCopy(const class_ro_t *ro) {
return extAlloc(ro, true);
}
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
};
在上面代码中我们看到有许多method_array_t
、property_array_t
、protocol_array_t
。这个是否就是所谓的方法
、属性
、协议
呢?
那我们就先探究下这里的bits
。
根据底层指针与取值的关系,我们知道在内存中平移指针可以取到不同内存区的值,所以我们是否可以利用这个方法来获取class_data_bits_t bits
保存的内容?
我们分析下:
在结构体objc_class
中我们知道了 isa
是8
字节、Class superclass
是个结构体指针所以是 8
字节,但是cache
多大我们不知道,所以我们先看看cache
。
cache_t
:
struct cache_t {
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask;
union {
struct {
explicit_atomic<mask_t> _maybeMask;
#if __LP64__
uint16_t _flags;
#endif
uint16_t _occupied;
};
explicit_atomic<preopt_cache_t *> _originalPreoptCache;
};
/*
*此处省略了大量不占用类内存的方法以及其他代码
*/
};
从上面代码可以看到内部占内存的就是一个联合体union
,并且根据互斥原则 我们只需要看explicit_atomic<preopt_cache_t *> _originalPreoptCache;
的大小或者算处另外三个的大小之和就好了,我们看到mask_t
是 uint32_t
类型所以是 4字节,其他两个 uint16_t
都是 2
字节
所以联合体总共8
字节,加上外部的explicit_atomic<uintptr_t> _bucketsAndMaybeMask
,这个主要看uintptr_t
的大小,我们知道这个是个指针所以 也是8
字节,所以总共是8+4+2+2 = 16
字节。
#if __LP64__
typedef uint32_t mask_t; // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t SEL;
所以
cache_t
为16
字节
所以在结构体
objc_class
中我们可以从首地址往下平移就可以得到class_data_bits_t
,现在我们知道了isa
是8
字节、Class superclass
是个结构体指针所以是8
字节,cache_t
为16
字节,所以我们可以利用地址平移上面的总和isa 8 + superclass 8 + cache_t 16 = 32 字节
来获取剩下的class_data_bits_t bits
保存的内容。
ps:这里开始以下的代码都必须在我们下载下来的objc
源码里来调试(因为自己创建的工程不包含class_data_bits_t
这些东西所以你取的时候会报 未声明的class_data_bits_t
)
直接上代码:
#import <Foundation/Foundation.h>
#import "ZYPerson.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 类的内存结构
// 8 + 8 + 16 = 32
ZYPerson *person = [[ZYPerson alloc] init];
// class_data_bits_t
}
return 0;
}
(lldb) x/4gx ZYPerson.class
0x1000086e0: 0x00000001000086b8 0x000000010036a140
0x1000086f0: 0x0000000101409bd0 0x0001801000000003
(lldb) p/x 0x1000086e0+0x20
(long) $1 = 0x0000000100008700
(lldb) p (class_data_bits_t *)0x0000000100008700
(class_data_bits_t *) $2 = 0x0000000100008700
(lldb)
代码解释图:

我们之前看objc
源码的时候看到bits
取值首先是从一个data()
里取如下:
struct objc_class : objc_object {
//省略前面的代码
class_rw_t *data() const {
return bits.data();
}
//省略后面的代码
};
所以我们利用前面得到的强转对象$6
来取data()
:
(lldb) x/4gx ZYPerson.class
0x1000086e0: 0x00000001000086b8 0x000000010036a140
0x1000086f0: 0x000000010070d450 0x0001803000000003
(lldb) p/x 0x1000086e0+0x20
(long) $1 = 0x0000000100008700
(lldb) p (class_data_bits_t *)0x0000000100008700
(class_data_bits_t *) $2 = 0x0000000100008700
(lldb) p $2->data()
(class_rw_t *) $3 = 0x000000010070c570
(lldb) p *$3
(class_rw_t) $4 = {
flags = 2148007936
witness = 1
ro_or_rw_ext = {
std::__1::atomic<unsigned long> = {
Value = 4295001072
}
}
firstSubclass = nil
nextSiblingClass = NSUUID
}
(lldb)
图文代码解释:

通过上面的代码我们发现到现在还没看到我们在ZYPerson
类里设置的属性、方法
等如图

这个时候我们回到class_rw_t
结构体源码中找方法我们可以看到以下代码:
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
所以我们可以知道取值方法原来在这里,我们可以看到都是通过properties来取值下一层的。接下来我们根据这些方法对应去取值查看是否能找到我们在ZYPerson
类里设置的属性和方法等。
属性:
我们根据代码property_array_t
跟踪进去可以看到:
class property_array_t :
public list_array_tt<property_t, property_list_t, RawPtr>
{
typedef list_array_tt<property_t, property_list_t, RawPtr> Super;
public:
property_array_t() : Super() { }
property_array_t(property_list_t *l) : Super(l) { }
};
接着跟踪list_array_tt
class list_array_tt {
struct array_t {
uint32_t count;
Ptr<List> lists[0];
static size_t byteSize(uint32_t count) {
return sizeof(array_t) + count*sizeof(lists[0]);
}
size_t byteSize() {
return byteSize(count);
}
};
protected:
class iterator {
const Ptr<List> *lists;
const Ptr<List> *listsEnd;
typename List::iterator m, mEnd;
public:
iterator(const Ptr<List> *begin, const Ptr<List> *end)
: lists(begin), listsEnd(end)
{
if (begin != end) {
m = (*begin)->begin();
mEnd = (*begin)->end();
}
}
const Element& operator * () const {
return *m;
}
Element& operator * () {
return *m;
}
bool operator != (const iterator& rhs) const {
if (lists != rhs.lists) return true;
if (lists == listsEnd) return false; // m is undefined
if (m != rhs.m) return true;
return false;
}
const iterator& operator ++ () {
ASSERT(m != mEnd);
m++;
if (m == mEnd) {
ASSERT(lists != listsEnd);
lists++;
if (lists != listsEnd) {
m = (*lists)->begin();
mEnd = (*lists)->end();
}
}
return *this;
}
};
public:
union {
Ptr<List> list;
uintptr_t arrayAndFlag;
};
bool hasArray() const {
return arrayAndFlag & 1;
}
array_t *array() const {
return (array_t *)(arrayAndFlag & ~1);
}
void setArray(array_t *array) {
arrayAndFlag = (uintptr_t)array | 1;
}
void validate() {
for (auto cursor = beginLists(), end = endLists(); cursor != end; cursor++)
cursor->validate();
}
public:
list_array_tt() : list(nullptr) { }
list_array_tt(List *l) : list(l) { }
list_array_tt(const list_array_tt &other) {
*this = other;
}
list_array_tt &operator =(const list_array_tt &other) {
if (other.hasArray()) {
arrayAndFlag = other.arrayAndFlag;
} else {
list = other.list;
}
return *this;
}
uint32_t count() const {
uint32_t result = 0;
for (auto lists = beginLists(), end = endLists();
lists != end;
++lists)
{
result += (*lists)->count;
}
return result;
}
iterator begin() const {
return iterator(beginLists(), endLists());
}
iterator end() const {
auto e = endLists();
return iterator(e, e);
}
inline uint32_t countLists(const std::function<const array_t * (const array_t *)> & peek) const {
if (hasArray()) {
return peek(array())->count;
} else if (list) {
return 1;
} else {
return 0;
}
}
uint32_t countLists() {
return countLists([](array_t *x) { return x; });
}
const Ptr<List>* beginLists() const {
if (hasArray()) {
return array()->lists;
} else {
return &list;
}
}
const Ptr<List>* endLists() const {
if (hasArray()) {
return array()->lists + array()->count;
} else if (list) {
return &list + 1;
} else {
return &list;
}
}
void attachLists(List* const * addedLists, uint32_t addedCount) {
if (addedCount == 0) return;
if (hasArray()) {
// many lists -> many lists
uint32_t oldCount = array()->count;
uint32_t newCount = oldCount + addedCount;
array_t *newArray = (array_t *)malloc(array_t::byteSize(newCount));
newArray->count = newCount;
array()->count = newCount;
for (int i = oldCount - 1; i >= 0; I--)
newArray->lists[i + addedCount] = array()->lists[I];
for (unsigned i = 0; i < addedCount; I++)
newArray->lists[i] = addedLists[I];
free(array());
setArray(newArray);
validate();
}
else if (!list && addedCount == 1) {
// 0 lists -> 1 list
list = addedLists[0];
validate();
}
else {
// 1 list -> many lists
Ptr<List> oldList = list;
uint32_t oldCount = oldList ? 1 : 0;
uint32_t newCount = oldCount + addedCount;
setArray((array_t *)malloc(array_t::byteSize(newCount)));
array()->count = newCount;
if (oldList) array()->lists[addedCount] = oldList;
for (unsigned i = 0; i < addedCount; I++)
array()->lists[i] = addedLists[I];
validate();
}
}
void tryFree() {
if (hasArray()) {
for (uint32_t i = 0; i < array()->count; i++) {
try_free(array()->lists[I]);
}
try_free(array());
}
else if (list) {
try_free(list);
}
}
template<typename Other>
void duplicateInto(Other &other) {
if (hasArray()) {
array_t *a = array();
other.setArray((array_t *)memdup(a, a->byteSize()));
for (uint32_t i = 0; i < a->count; i++) {
other.array()->lists[i] = a->lists[i]->duplicate();
}
} else if (list) {
other.list = list->duplicate();
} else {
other.list = nil;
}
}
}
从上面的代码我们可以发现list_array_tt
方法里主要是有一个Ptr<List> lists[0]
和一个迭代器方法
/*****删除其他代码******/
public:
iterator(const Ptr<List> *begin, const Ptr<List> *end)
: lists(begin), listsEnd(end)
{
if (begin != end) {
m = (*begin)->begin();
mEnd = (*begin)->end();
}
}
const Element& operator * () const {
return *m;
}
Element& operator * () {
return *m;
}
/*****删除其他代码******/
从这个方法里我们可以看到是从初始值位置一直到结束值位置依次遍历并且去处其中元素,所以我们也可以这样取值,根据前面的打印接着走:
(lldb) p $3.properties()
(const property_array_t) $5 = {
list_array_tt<property_t, property_list_t, RawPtr> = {
= {
list = {
ptr = 0x0000000100008540
}
arrayAndFlag = 4295001408
}
}
}
Fix-it applied, fixed expression was:
$3->properties()
(lldb) p $5.list
(const RawPtr<property_list_t>) $6 = {
ptr = 0x0000000100008540
}
(lldb) p $6.ptr
(property_list_t *const) $7 = 0x0000000100008540
(lldb) p *$7
(property_list_t) $8 = {
entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $8.get(0)
(property_t) $9 = (name = "zyName", attributes = "T@\"NSString\",C,N,V_zyName")
(lldb) p $8.get(1)
(property_t) $10 = (name = "zyMutArray", attributes = "T@\"NSMutableArray\",&,N,V_zyMutArray")
(lldb)
图文解析代码:

至此,我们已经取到了类属性,层级分别是:
NSObject.class -> class_data_bits_t -> class_rw_t -> property_array_t -> RawPtr<property_list_t> -> property_list_t -> property_t
下面我们可以用同样的方法取得实例方法、类方法、成员变量
实例方法:
在这之前我们也分析下方法的获取方法
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
根据代码跟踪发现下层代码和属性有些不同在于:
class method_array_t :
public list_array_tt<method_t, method_list_t, method_list_t_authed_ptr>
{
typedef list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> Super;
public:
method_array_t() : Super() { }
method_array_t(method_list_t *l) : Super(l) { }
const method_list_t_authed_ptr<method_list_t> *beginCategoryMethodLists() const {
return beginLists();
}
const method_list_t_authed_ptr<method_list_t> *endCategoryMethodLists(Class cls) const;
};
struct method_list_t : entsize_list_tt<method_t, method_list_t, 0xffff0003, method_t::pointer_modifier> {
bool isUniqued() const;
bool isFixedUp() const;
void setFixedUp();
uint32_t indexOfMethod(const method_t *meth) const {
uint32_t I =
(uint32_t)(((uintptr_t)meth - (uintptr_t)this) / entsize());
ASSERT(i < count);
return I;
}
bool isSmallList() const {
return flags() & method_t::smallMethodListFlag;
}
bool isExpectedSize() const {
if (isSmallList())
return entsize() == method_t::smallSize;
else
return entsize() == method_t::bigSize;
}
method_list_t *duplicate() const {
method_list_t *dup;
if (isSmallList()) {
dup = (method_list_t *)calloc(byteSize(method_t::bigSize, count), 1);
dup->entsizeAndFlags = method_t::bigSize;
} else {
dup = (method_list_t *)calloc(this->byteSize(), 1);
dup->entsizeAndFlags = this->entsizeAndFlags;
}
dup->count = this->count;
std::copy(begin(), end(), dup->begin());
return dup;
}
};
根据属性的层次分析我们知道最终我们的得到的其实是property_t
,那么我们看到上面关于方法的底层也有一个 method_t
,我们跟踪进去看看:
struct method_t {
static const uint32_t smallMethodListFlag = 0x80000000;
method_t(const method_t &other) = delete;
// The representation of a "big" method. This is the traditional
// representation of three pointers storing the selector, types
// and implementation.
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
//*******省略了其他代码*******
}
发现多了一层big,big这一层就跟 property_t 一样是一个结构体
struct property_t {
const char *name;
const char *attributes;
};
这样我们就知道怎么取方法了。上代码:
(lldb) x/4gx ZYPerson.class
0x1000086e0: 0x00000001000086b8 0x000000010036a140
0x1000086f0: 0x0000000101237340 0x0001803000000003
(lldb) p/x 0x1000086e0+0x20
(long) $1 = 0x0000000100008700
(lldb) p (class_data_bits_t *)0x0000000100008700
(class_data_bits_t *) $2 = 0x0000000100008700
(lldb) p $2->data()
(class_rw_t *) $3 = 0x00000001012372f0
(lldb) p $3.methods()
(const method_array_t) $4 = {
list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
= {
list = {
ptr = 0x0000000100008438
}
arrayAndFlag = 4295001144
}
}
}
Fix-it applied, fixed expression was:
$3->methods()
(lldb) p $4.list
(const method_list_t_authed_ptr<method_list_t>) $5 = {
ptr = 0x0000000100008438
}
(lldb) p $5.ptr
(method_list_t *const) $6 = 0x0000000100008438
(lldb) p *$6
(method_list_t) $7 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 5)
}
(lldb) p $7.get(0).big()
(method_t::big) $8 = {
name = "zyEatSugar"
types = 0x0000000100003f26 "v16@0:8"
imp = 0x0000000100003c50 (KCObjcBuild`-[ZYPerson zyEatSugar])
}
(lldb) p $7.get(1).big()
(method_t::big) $9 = {
name = "zyName"
types = 0x0000000100003f1e "@16@0:8"
imp = 0x0000000100003c80 (KCObjcBuild`-[ZYPerson zyName])
}
(lldb) p $7.get(2).big()
(method_t::big) $10 = {
name = "setZyName:"
types = 0x0000000100003f3a "v24@0:8@16"
imp = 0x0000000100003cb0 (KCObjcBuild`-[ZYPerson setZyName:])
}
(lldb) p $7.get(3).big()
(method_t::big) $11 = {
name = "zyMutArray"
types = 0x0000000100003f1e "@16@0:8"
imp = 0x0000000100003ce0 (KCObjcBuild`-[ZYPerson zyMutArray])
}
(lldb) p $7.get(4).big()
(method_t::big) $12 = {
name = "setZyMutArray:"
types = 0x0000000100003f3a "v24@0:8@16"
imp = 0x0000000100003d00 (KCObjcBuild`-[ZYPerson setZyMutArray:])
}
(lldb)
至此,我们已经取到了实例方法以及
setter\getter
方法,层级是:NSObject.class -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t_authed_ptr<method_list_t> -> method_list_t -> big() -> method_t
但是没有看到类方法和成员变量
成员变量:
我们分析struct class_rw_t {}
结构体的时候并没有发现有显著标明成员变量和类方法的地方。但是我们在WWDC
上有一段视频告诉我们OC/swift
底层有些改变。我们根据他所说的去探索下。他提到有些东西放在了class_ro_t
,那我们跟着去找找class_ro_t
.同样在代码看到:
struct class_rw_ext_t {
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
}
我们就根据set get
方法来获取,代码如下:
(lldb) x/4gx ZYPerson.class
0x1000086e0: 0x00000001000086b8 0x000000010036a140
0x1000086f0: 0x0000000101223370 0x0001803000000003
(lldb) p/x 0x1000086e0+0x20
(long) $1 = 0x0000000100008700
(lldb) p (class_data_bits_t *)0x0000000100008700
(class_data_bits_t *) $2 = 0x0000000100008700
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000101223190
(lldb) p *$3
(class_rw_t) $4 = {
flags = 2148007936
witness = 1
ro_or_rw_ext = {
std::__1::atomic<unsigned long> = {
Value = 4295001072
}
}
firstSubclass = nil
nextSiblingClass = NSUUID
}
(lldb) p $4.ro()
(const class_ro_t *) $5 = 0x00000001000083f0
(lldb) p $5.ivars
(const ivar_list_t *const) $6 = 0x00000001000084b8
Fix-it applied, fixed expression was:
$5->ivars
(lldb) p *$6
(const ivar_list_t) $7 = {
entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 4)
}
(lldb) p $7.get(0)
(ivar_t) $8 = {
offset = 0x00000001000085a8
name = 0x0000000100003ec3 "zyfit"
type = 0x0000000100003f2e "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(1)
(ivar_t) $9 = {
offset = 0x00000001000085b0
name = 0x0000000100003ec9 "zySubject"
type = 0x0000000100003f45 "@\"NSObject\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(2)
(ivar_t) $10 = {
offset = 0x00000001000085b8
name = 0x0000000100003ed3 "_zyName"
type = 0x0000000100003f2e "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7.get(3)
(ivar_t) $11 = {
offset = 0x00000001000085c0
name = 0x0000000100003edb "_zyMutArray"
type = 0x0000000100003f51 "@\"NSMutableArray\""
alignment_raw = 3
size = 8
}
至此,我们已经取到了成员变量,层级是:
NSObject.class -> class_data_bits_t -> class_rw_t -> class_ro_t -> ivar_list_t<ivar_t>
类方法:
上代码:
(lldb) x/4gx ZYPerson.class
0x1000086e0: 0x00000001000086b8 0x000000010036a140
0x1000086f0: 0x00000001013c64f0 0x0001803000000003
(lldb) p/x 0x00000001000086b8 & 0x00007ffffffffff8
(long) $1 = 0x00000001000086b8
(lldb) x/4gx 0x00000001000086b8
0x1000086b8: 0x000000010036a0f0 0x000000010036a0f0
0x1000086c8: 0x0000000100646a20 0x0002e03100000003
(lldb) p/x 0x1000086b8+0x20
(long) $2 = 0x00000001000086d8
(lldb) p (class_data_bits_t *)0x00000001000086d8
(class_data_bits_t *) $3 = 0x00000001000086d8
(lldb) p $3->data()
(class_rw_t *) $4 = 0x00000001013c6480
(lldb) p $4.methods()
(const method_array_t) $5 = {
list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
= {
list = {
ptr = 0x00000001000083d0
}
arrayAndFlag = 4295001040
}
}
}
Fix-it applied, fixed expression was:
$4->methods()
(lldb) p $5.list
(const method_list_t_authed_ptr<method_list_t>) $6 = {
ptr = 0x00000001000083d0
}
(lldb) p $6.ptr
(method_list_t *const) $7 = 0x00000001000083d0
(lldb) p *$7
(method_list_t) $8 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
}
(lldb) p $8.get(0).big()
(method_t::big) $9 = {
name = "zyEatSomething"
types = 0x0000000100003f26 "v16@0:8"
imp = 0x0000000100003c20 (KCObjcBuild`+[ZYPerson zyEatSomething])
}
(lldb)
代码图解:


至此,我们已经取到了成员变量,层级是:
NSObject.class -> metaClass -> class_data_bits_t -> class_rw_t -> method_array_t -> method_list_t_authed_ptr<method_list_t> -> method_list_t -> big() -> method_t
补充:
1,内存偏移
首先我们来看一段代码:
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
int b = 10;
NSLog(@"a:%d -- %p",a,&a);
NSLog(@"b:%d -- %p",b,&b);
//对象
ZYPerson *p1 = [[ZYPerson alloc] init];
ZYPerson *p2 = [[ZYPerson alloc] init];
NSLog(@"p1:%@ -- %p",p1,&p1);
NSLog(@"p2:%@ -- %p",p2,&p2);
}
return 0;
}
2021-06-22 18:38:56.558556+0800 ZYProjectFour001[98912:3361252] a:10 -- 0x7ffeefbff3ac
2021-06-22 18:38:56.559010+0800 ZYProjectFour001[98912:3361252] b:10 -- 0x7ffeefbff3a8
从上面代码我们可以看到a
和b
地址不一样,但是值是一样的,这表明他们的地址指向同一片内存值(常量10
)。
对象p1
,p2
对象指针地址不一样p1 : 0x105a05af0
、p2 : 0x105a05b60
,并且指向的空间也不同分别为:0x7ffeefbff3a0
、0x7ffeefbff398
。
所以,他们的指针以及指向内存的情况应该是如下图:

接下来我们再看一组代码:
int main(int argc, const char * argv[]) {
@autoreleasepool {
//数组指针
int c[4] = {1,2,3,4};
int *d = c;
NSLog(@"c:%p -- %p -- %p",&c,&c[0],&c[1]);
NSLog(@"d:%p -- %p -- %p",d,d+1,d+2);
for (int i = 0; i<4; i++) {
int value = c[I];
NSLog(@"value == %d",value);
NSLog(@"value2 == %d",*(d+i));
}
}
return 0;
}
2021-06-22 18:38:56.559451+0800 ZYProjectFour001[98912:3361252] c:0x7ffeefbff3c0 -- 0x7ffeefbff3c0 -- 0x7ffeefbff3c4
2021-06-22 18:38:56.559490+0800 ZYProjectFour001[98912:3361252] d:0x7ffeefbff3c0 -- 0x7ffeefbff3c4 -- 0x7ffeefbff3c8
2021-06-22 18:38:56.559547+0800 ZYProjectFour001[98912:3361252] value == 1
2021-06-22 18:38:56.559593+0800 ZYProjectFour001[98912:3361252] value2 == 1
2021-06-22 18:38:56.559627+0800 ZYProjectFour001[98912:3361252] value == 2
2021-06-22 18:38:56.559658+0800 ZYProjectFour001[98912:3361252] value2 == 2
2021-06-22 18:38:56.559690+0800 ZYProjectFour001[98912:3361252] value == 3
2021-06-22 18:38:56.559721+0800 ZYProjectFour001[98912:3361252] value2 == 3
2021-06-22 18:38:56.559752+0800 ZYProjectFour001[98912:3361252] value == 4
2021-06-22 18:38:56.559781+0800 ZYProjectFour001[98912:3361252] value2 == 4
从上面的代码我们发现
&c
和&c[0]
是同一个地址0x7ffeefbff3c0
。
并且我们发现d
、d+1
的地址又和&c
、&c[1]
相同。所以他们指向的是同一片内存。d -> d+1 -> d+2
这种方式就是把数组指针从第一个元素移动到第二个第三个元素的地址。这就是指针平移
。
本章内容就算是告一段落,下一章我们继续。
遇事不决,可问春风。站在巨人的肩膀上学习,如有疏忽或者错误的地方还请多多指教。谢谢!
网友评论