前言
通过本篇文章可以了解
1.isa的走位
2.类结构的分析
3.什么是元类
4.supclass的走位
5.objc_class & objc_object
一objc_class & objc_object
@interface person : NSObject
{
NSString *hobby;
}
@property (nonatomic, copy) NSString *cjl_name;
- (void)sayHello;
+ (void)sayBye;
@end
@implementation person
- (void)sayHello
{}
+ (void)sayBye
{}
@end
@interface student : person
@end
@implementation student
@end
我们首先定义好的类
打开之前编译好的objc4-781
的源码,找到objc_class
结构体定义
然后再去他的父结构体看看
- 类的本质是一个结构体,有一个
objc_class
类型的isa
属性 -
objc_class
继承于另一个结构体objc_object
,类的isa
属性是从objc_object
继承过来的,所以每个类都被赋予isa属性
然后我们来看看objc_class
这个结构体
struct objc_class : objc_object {
// Class ISA; //8字节
Class superclass; //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
: 缓存指针和函数表 8字节(不作为本章重点) -
bits
: 属性、方法等信息 8字节(一会深入探索)
通过指针偏移读取类内存中bits 中的信息
我们可以通过偏移32
字节来获取到bits的信息
step.1 通过16进制打印获取类地址
(lldb) p/x objc1.class
(Class) $3 = 0x0000000100008458 person
step.2 0x0000000100008458
偏移 32字节 得到 0x0000000100008478
(lldb) p (class_data_bits_t *)0x0000000100008478
(class_data_bits_t *) $4 = 0x0000000100008478
step.3 通过提供的data()
方法来获取信息 ,查看data
当中都有什么
(lldb) p $4->data()
(class_rw_t *) $6 = 0x000000010064e610
(lldb) p *$6
(class_rw_t) $7 = {
flags = 2148007936
witness = 1
ro_or_rw_ext = {
std::__1::atomic = {
Value = 4295000232
}
}
firstSubclass = student
nextSiblingClass = NSUUID
}
(lldb)
暂时看不懂 不过有提供method()
看起来应该是获取方法的列表 我们打印一下看看
step.4调用Methods()
(lldb) p $6->methods()
(const method_array_t) $10 = {
list_array_tt = {
= {
list = 0x00000001000080f0
arrayAndFlag = 4295000304
}
}
}
step.5 调用list
属性 查看list
(lldb) p $10.list
(method_list_t *const) $11 = 0x00000001000080f0
(lldb) p *$11
(method_list_t) $12 = {
entsize_list_tt = {
entsizeAndFlags = 26
count = 4
first = {
name = "sayHello"
types = 0x0000000100003ef9 "v16@0:8"
imp = 0x0000000100003b80 (KCObjc`-[person sayHello] at main.m:24)
}
}
}
到了这里我们就可以看到我们的sayhello
实例方法存在bits里边通过get()
方法验证一下
step.6查看get()
(lldb) p $12.get(0)
(method_t) $13 = {
name = "sayHello"
types = 0x0000000100003ef9 "v16@0:8"
imp = 0x0000000100003b80 (KCObjc`-[person sayHello] at main.m:24)
}
(lldb) p $12.get(1)
(method_t) $14 = {
name = "cjl_name"
types = 0x0000000100003f0d "@16@0:8"
imp = 0x0000000100003b90 (KCObjc`-[person cjl_name] at main.m:18)
}
(lldb) p $12.get(2)
(method_t) $15 = {
name = "setCjl_name:"
types = 0x0000000100003f15 "v24@0:8@16"
imp = 0x0000000100003bc0 (KCObjc`-[person setCjl_name:] at main.m:18)
}
(lldb) p $12.get(3)
(method_t) $16 = {
name = ".cxx_destruct"
types = 0x0000000100003ef9 "v16@0:8"
imp = 0x0000000100003bf0 (KCObjc`-[person .cxx_destruct] at main.m:23)
}
(lldb) p $12.get(4)
Assertion failed: (i < count), function get, file ~/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.7 我们发现在class_rw_t
类型当中发现了properties()
p $6->properties()
(const property_array_t) $17 = {
list_array_tt = {
= {
list = 0x00000001000081a0
arrayAndFlag = 4295000480
}
}
}
(lldb) p $17.list
(property_list_t *const) $18 = 0x00000001000081a0
(lldb) p *$18
(property_list_t) $19 = {
entsize_list_tt = {
entsizeAndFlags = 16
count = 1
first = (name = "cjl_name", attributes = "T@\"NSString\",C,N,V_cjl_name")
}
}
step.7在通过get()
方法查看一下
(lldb) p $19.get(0)
(property_t) $20 = (name = "cjl_name", attributes = "T@\"NSString\",C,N,V_cjl_name")
(lldb) p $19.get(1)
Assertion failed: (i < count), function get, file ~/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.
这次是到第2
个就数组越界了
属性存储在bits
通过 bits -> data() -> properties() -> list
获取
实例方法存储在bits
通过bits -> data() -> methods() -> list
获取
类方法探索
猜测一下,对象的isa
指向了类,类里有对象方法,类的isa
指向元类,类方法会不会在元类里呢?
step.1 获取元类
(lldb) x/4gx objc1.class
0x100008458: 0x0000000100008430 0x000000010034c140
0x100008468: 0x0000000101831230 0x0002802400000003
(lldb) p/x 0x0000000100008430 * 0x0000000ffffffff8ULL
(unsigned long long) $1 = 0x000842f7fffbde80
step.20x000842f7fffbde80
地址偏移32
字节得到0x000842f7fffbdea0
(lldb) p (class_data_bits_t *)0x000842f7fffbdea0
(class_data_bits_t *) $2 = 0x000842f7fffbdea0
step.3调用data()
,获取class_rw_t* data
,看下data
的信息
(lldb) p $5->data()
(class_rw_t *) $6 = 0x0000000101077020
(lldb) p *$6
(class_rw_t) $8 = {
flags = 2684878849
witness = 1
ro_or_rw_ext = {
std::__1::atomic = {
Value = 4295000128
}
}
firstSubclass = 0x0000000100008480
nextSiblingClass = 0x00007fff85d59cd8
}
step.4 查看methods()
打印list
查看list
内容
(lldb) p $6->methods()
(const method_array_t) $7 = {
list_array_tt = {
= {
list = 0x0000000100008088
arrayAndFlag = 4295000200
}
}
}
lldb) p $7.list
(method_list_t *const) $9 = 0x0000000100008088
(lldb) p *$9
(method_list_t) $10 = {
entsize_list_tt = {
entsizeAndFlags = 26
count = 1
first = {
name = "sayBye"
types = 0x0000000100003ef9 "v16@0:8"
imp = 0x0000000100003b70 (KCObjc`+[person sayBye] at main.m:26)
}
}
}
step.5查看一下通过get()
查看一下
(lldb) p $10.get(0)
(method_t) $11 = {
name = "sayBye"
types = 0x0000000100003ef9 "v16@0:8"
imp = 0x0000000100003b70 (KCObjc`+[person sayBye] at main.m:26)
}
(lldb) p $10.get(1)
Assertion failed: (i < count), function get, file ~/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.
总结
可以看到我们的类方法都是存储在元类当中,
通过元类bits -> methods() -> list
获取类方法
通过类bits -> methods() -> list
获取到对象方法