学习OC时,常听的就是万物皆对象,对象都有isa指针,那为什么有isa指针,isa指针到底是谁创建的?怎么创建的?我们来探索一下
探索的方法
使用Clang或者xcrun将.m文件转换成.cpp文件,通过.cpp文件观察底层的方法和实现
Clang是由苹果主导编写,基于LLVM 的C/C++/OC的编译器
xcode安装的时候顺带安装了xcrun命令、xcrun命令在Clang的基础上进行了一些封装,要更好一些
打开终端,cd到当前文件下,利用以下几种命令行可以编译成.cpp文件
创建一个继承于NSObject的Person类,添加一个字符串name属性
//使用xcode工具 xcrun
//1、指定模拟器文件编译
xcrun -sdk iphonesimulator clang -arch x86_64 -rewrite-objc Person.m -o Person.cpp
//2、指定真机文件编译
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person.m -o Person-arm64.cpp
//使用Clang 将 main.m 编译成 main.cpp
clang -rewrite-objc main.m -o main.cpp
objc_class& objc_object
打开编译好的.cpp文件,内容多的不知道从何入手了,不急,一步步来
- 搜索我们的类名
Person,看看能发现啥

我找了OC的源码,但是被注释掉了,在它的上面发现了Person_IMPL这个结构体,它包含了我们的属性name,由此可证明,我的Person类被编译成了结构体,这个结构体中,不仅包含了name,还有一个struct NSObject_IMPL NSObject_IVARS;找到NSObject_IMPL的定义

- 打开我们之前编译好的
objc4-781的源码,搜索Class isa,cmd Class找到它的定义

-
cmd objc_class,来到objc_class的源码

objc_class 没有isa啊,进它的父类objc_object看看

- 类的本质是一个结构体,有一个
objc_class类型的isa属性 -
objc_class继承于另一个结构体objc_object,类的isa属性是从objc_object继承过来的 -
objc_object就是根源类

objc_class结构分析
struct objc_class : objc_object {
// Class ISA; //8字节
Class superclass; //Class 类型 8字节
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
//....方法部分省略
}
-
isa:继承于objc_object的isa,8字节 -
superclass:父类,本质objc_class结构体指针, 8字节 -
cache: 缓存指针和函数表(下一章深入探索) -
bits: 属性、方法等信息 (一会深入探索)
cache 内存大小
cache是cache_t类型,看下cache_t的源码

-
buckets类型是struct bucket_t *,是结构体指针类型,占8字节 -
mask是mask_t类型,而mask_t是unsigned int的别名,占4字节 -
_maskAndBuckets是uintptr_t类型,它是一个指针,占8字节 -
_mask_unused是mask_t类型,而mask_t是uint32_t类型定义的别名,占4字节
无论走if还是 else if 都是16字节
通过指针偏移读取类内存中bits 中的信息
bits = 类的首地址偏移32字节
上一小节中我们提到bits存储的属性、方法等信息,现在我们添加一个成员变量、一个类方法、一个对象方法

接下来打印一下bits
step 1. 获取类地址
(lldb) p/x person.class
(Class) $0 = 0x00000001000022d8 Person
step 2. 首地址偏移32字节,32字节转16进制 20
0x00000001000022d8 -> 0x00000001000022f8,打印时,强转成bits的类型,强转以后我们才能调用class_data_bits_t的方法
(lldb) p (class_data_bits_t *)0x00000001000022f8
(class_data_bits_t *) $1 = 0x00000001000022f8
step 3. 调用class_data_bits_t提供的data()方法,获取class_rw_t* data,看下data的信息
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000102004680
(lldb) p *$2
(class_rw_t) $3 = {
flags = 2148007936
witness = 1
ro_or_rw_ext = {
std::__1::atomic<unsigned long> = 4294975800
}
firstSubclass = nil
nextSiblingClass = NSUUID
}
暂时看不懂,class_rw_t提供了一个方法methods(),看起来应该是获取方法列表
step 4.调用methods,看下输出
(lldb) p $3.methods()
(const method_array_t) $4 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x0000000100002180
arrayAndFlag = 4294975872
}
}
}
返回的是method_array_t 继承于 list_array_tt,使用 list_array_tt提供的list属性,打印
(lldb) p $4.list
(method_list_t *const) $5 = 0x0000000100002180
(lldb) p *$5
(method_list_t) $6 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 4
first = {
name = "objcMethod"
types = 0x0000000100000f80 "v16@0:8"
imp = 0x0000000100000dd0 (Example`-[Person objcMethod])
}
}
}
count = 4,证明我们有4个方法,分别打印一下其它的方法
(lldb) p $6.get(0)
(method_t) $7 = {
name = "objcMethod"
types = 0x0000000100000f80 "v16@0:8"
imp = 0x0000000100000dd0 (Example`-[Person objcMethod])
}
(lldb) p $6.get(1)
(method_t) $8 = {
name = ".cxx_destruct"
types = 0x0000000100000f80 "v16@0:8"
imp = 0x0000000100000e40 (Example`-[Person .cxx_destruct])
}
(lldb) p $6.get(2)
(method_t) $9 = {
name = "name"
types = 0x0000000100000f94 "@16@0:8"
imp = 0x0000000100000de0 (Example`-[Person name])
}
(lldb) p $6.get(3)
(method_t) $10 = {
name = "setName:"
types = 0x0000000100000f9c "v24@0:8@16"
imp = 0x0000000100000e10 (Example`-[Person setName:])
}
(lldb) p $6.get(4)
Assertion failed: (i < count), function get, file /Users/star/Desktop/objc4-781/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
打印第5个元素时,越界了,那我们的类方法去哪了?一会再分析,我们再去看看属性
step 5.class_rw_t提供了一个方法properties(),看起来应该是获取属性列表的
(lldb) p $3.properties()
(const property_array_t) $11 = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x0000000100002230
arrayAndFlag = 4294976048
}
}
}
返回的是property_array_t 继承于 list_array_tt,使用 list_array_tt提供的list属性,打印
(lldb) p $11.list
(property_list_t *const) $12 = 0x0000000100002230
(lldb) p *$12
(property_list_t) $13 = {
entsize_list_tt<property_t, property_list_t, 0> = {
entsizeAndFlags = 16
count = 1
first = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
}
}
count = 1,不会吧,只有一个属性?分别打印试下
(lldb) p $13.get(0)
(property_t) $14 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $13.get(1)
Assertion failed: (i < count), function get, file /Users/star/Desktop/objc4-781/runtime/objc-runtime-new.h, line 438.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
我们的成员变量也丢了
step 6.再看下class_rw_t的定义,还有一个ro的方法,返回了一个class_ro_t类型的结构体,class_ro_t有一个ivars属性,看着比较像,打印一下
(lldb) p $3.ro()
(const class_ro_t *) $15 = 0x0000000100002138
(lldb) p *$15
(const class_ro_t) $16 = {
flags = 388
instanceStart = 8
instanceSize = 24
reserved = 0
ivarLayout = 0x0000000100000f3f "\x02"
name = 0x0000000100000f38 "Person"
baseMethodList = 0x0000000100002180
baseProtocols = 0x0000000000000000
ivars = 0x00000001000021e8
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100002230
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $16.ivars
(const ivar_list_t *const) $17 = 0x00000001000021e8
(lldb) p $17.get(0)
(ivar_t) $18 = {
offset = 0x0000000100002250
name = 0x0000000100000f4d "_cybl"
type = 0x0000000100000f88 "@\"NSString\""
alignment_raw = 3
size = 8
}
Fix-it applied, fixed expression was:
$17->get(0)
找到了 _cybl!
- 成员变量:存储在类的
bits属性中,通过bits -> data() ->ro() -> ivars获取 - 属性,也存储在
bits属性中,通过bits -> data() -> properties() -> list获取
探索类方法存储
猜测一下,对象的isa指向了类,类里有对象方法,类的isa指向元类,类方法会不会在元类里呢?
step 1.获取元类地址
(lldb) x/4gx Person.class
0x1000022d8: 0x00000001000022b0 0x0000000100333140
0x1000022e8: 0x0000000102118040 0x0001802400000003
(lldb) p/x 0x00000001000022b0 & 0x0000000ffffffff8ULL
(unsigned long long) $1 = 0x00000001000022b0
step 2. 首地址偏移32字节,32字节转16进制 20
(lldb) p (class_data_bits_t *)0x00000001000022d0
(class_data_bits_t *) $2 = 0x00000001000022d0
step 3.调用data(),获取class_rw_t* data,看下data的信息
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000101053d80
(lldb) p *$3
(class_rw_t) $4 = {
flags = 2684878849
witness = 1
ro_or_rw_ext = {
std::__1::atomic<unsigned long> = 4294975696
}
firstSubclass = nil
nextSiblingClass = 0x00007fff8c6b0c60
}
step 4.调用methods,看下输出
(const method_array_t) $5 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x0000000100002118
arrayAndFlag = 4294975768
}
}
}
返回的是method_array_t 继承于 list_array_tt,使用 list_array_tt提供的list属性,打印
(lldb) p $5.list
(method_list_t *const) $6 = 0x0000000100002118
(lldb) p *$6
(method_list_t) $7 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 1
first = {
name = "classMethod"
types = 0x0000000100000f80 "v16@0:8"
imp = 0x0000000100000dc0 (Example`+[Person classMethod])
}
}
}
找到了 classMethod
- 类的对象方法存储在
类的bits属性中,通过bits -> methods() -> list获取方法列表 - 类的类方法存储在
元类的bits属性中,通过元类bits -> methods() -> list获取方法列表
