学习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
获取方法列表
网友评论