今天我们经过源码的探索来分析下类的结构,我们从isa
和 类的继承
两方面来分析:
我们先看一个经典的走位图:
我们来验证下isa的走位:
首先我们先创建一个LGPerson
类然后用lldb
打印一验证:
@interface LGPerson : NSObject
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LGPerson *person = [LGPerson alloc];
NSLog(@"%@",person);
return 0;
}
(lldb) x/4gx person
: 打印对象的内存
0x10056bcb0: 0x001d800100008275 0x0000000000000000
0x10056bcc0: 0x0000000000000000 0x0000000000000000
0x001d800100008275
即为对象的isa
(lldb) p/x 0x001d800100008275 & 0x00007ffffffffff8ULL
:打印isa的指针
(unsigned long long) $1 = 0x0000000100008270(lldb) po 0x0000000100008270
: 打印isa 指针
LGPerson
这时候得到了LGPerson
这个类,说明isa 指向 类
(lldb) x/4gx 0x0000000100008270
: 接着打印LGPerson
这个类的内存
0x100008270: 0x0000000100008248 0x00007fff92b5e118
0x100008280: 0x00007fff6b38d140 0x0000802c00000000
得到类的isa
为0x0000000100008248
,然后我们获取这个isa
的指针(lldb) p/x 0x0000000100008248 & 0x00007ffffffffff8ULL
: 打印isa
的指针
(unsigned long long) $3 = 0x0000000100008248(lldb) po 0x0000000100008248
:打印这个指针
LGPerson
这是我们发现又得到一个LGPerson
,此时这个类就是元类
。
接着我们在去打印这个元类
的内存(lldb) x/4gx 0x0000000100008248
:
0x100008248: 0x00007fff92b5e0f0 0x00007fff92b5e0f0
0x100008258: 0x0000000100404760 0x0003e03500000007
此时我们获取到元类
的isa
,然后我们获取元类 isa
的指针(lldb) p/x 0x00007fff92b5e0f0 & 0x00007ffffffffff8ULL
:
(unsigned long long) $5 = 0x00007fff92b5e0f0
得到了元类 isa
的指针,然后我们去打印这个指针(lldb) po 0x00007fff92b5e0f0
:
NSObject
我们得到了一个NSObject
,那此时的这个NSObject
,到底是不是根类 NSObject
呢?我们验证一下
-(lldb) p/x NSObject.class
: 我们打印下根类
的地址指针
(Class) $8 = 0x00007fff92b5e118 NSObject
我们发现0x00007fff92b5e118
这个指针不等于上面我们获取到的0x00007fff92b5e0f0
这个指针,说明上面我们获取到的NSObject
不是根类,我们给这个NSObject
起了个名字叫根元类
。(lldb) x/4gx 0x00007fff92b5e0f0
: 继续打印根元类
的内存信息获取isa
0x7fff92b5e0f0: 0x00007fff92b5e0f0 0x00007fff92b5e118
0x7fff92b5e100: 0x0000000100607300 0x0005e03100000007
此时我们发现isa 0x00007fff92b5e0f0
和根元类
的指针地址是一样的,这时我们在打印下这个isa
(lldb) po 0x00007fff92b5e0f0
NSObject
此时我们又得到了NSObject
.由此可见,根元类的 isa指针 指向自己
。
此时我们完美验证了上图的isa
走向流程
isa总结:对象的isa 指向对象的类--> 对象的类的isa指向元类 ---> 元类的isa指向根元类 ---> 根元类的isa指向自己
类的继承
从上面的经典走位图里面我们也可以看出类
和元类
的继承关系:
类
的继承
类
继承于父类
父类
继承于根类
根类
继承于nil
元类
的继承
元类
继承于父元类
父元类
继承于根元类
根元类
继承于根类
根类
继承于nil
注
: 关于类的继承我们要明白一点:实例对象之间是没有继承关系的
,只有类 之间才有继承关系
。
通过下面的图我们也可以看到实例对象之间是没有继承关系的
万物皆对象
我们在上面的走位图中发现,对象
和类
、元类
中都有isa
,这是为什么呢? 这就引出了两个结构体_objc_class
和_objc_object
,我们对mian.m
进行clang
编译后我们会发现对象本质是一个结构体
,他的底层会被编译为NSObject_IMPL
而 这个NSObject_IMPL
,内部包含一个Class 定义的 isa
,而Class 是有 objc_class 定义的
:
struct LGPerson_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *_name;
};
struct NSObject_IMPL {
Class isa;
};
typedef struct objc_class *Class;
我们在源码中搜索objc_class
,发现objc_class
继承于_objc_object
,而_objc_object
里面包含一个默认的isa
:
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc }
}
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
}
从上面的源码我们也看出了,所有的对象
和类
都是从objc_object
继承过来的,所以都一定有isa
.
类的结构分析
我们先创建一个LGPerson
类,然后为它添加属性
、成员变量
、实例方法
、类方法
都存在类信息的那个部位:
@interface LGPerson : NSObject {
NSString *hobby;
}
@property (nonatomic, strong) NSString *name;
- (void)sayHello;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello{
NSLog(@"LGPerson say : Hello!!!");
}
+ (void)sayHappy{
NSLog(@"LGPerson say : Happy!!!");
}
@end
探索之前我们先看下类信息
里面都包含哪些东西:
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc }
}
isa
属性:继承自objc_object
的isa
,指针占 8 字节
superclass
属性:指向父类,指针占 8 字节
cache
属性:用于缓存方法的,用于加速方法的调用,占 16 字节,具体分析,看cache_t 结构体的分析
bits
属性:存储类的方法、属性、协议等信息的地方
我们看到bits
是存储类的信息的,而bits
是有class_data_bits_t
定义的,那我们就看下class_data_bits_t
的源码:
struct class_data_bits_t {
friend objc_class;
// Values are the FAST_ flags above.
uintptr_t bits;
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
// 代码自动省略
}
const class_ro_t *safe_ro() {
class_rw_t *maybe_rw = data();
if (maybe_rw->flags & RW_REALIZED) {
// maybe_rw is rw
return maybe_rw->ro();
} else {
// maybe_rw is actually ro
return (class_ro_t *)maybe_rw;
}
}
// 代码自动省略
我们主要看下class_rw_t
和class_ro_t
:
struct class_rw_t {
//这里只显示我们需要看到额属性
const method_array_t methods() const {}
const property_array_t properties() const {}
const protocol_array_t protocols() const { }
}
我们现在用lldb
来打印下bits
里面的信息:
-
首先平移32位 :
-
接着
class_data_bits_t
通过data()
获取class_rw_t
结构体指针:
- 然后我们根据
class_rw_t
中的methods()
、properties()
去获取 方法和属性:
我们发现打印出的方法只有sayHello
没有、属性只有name
,这是因为类方法
和成员变量
存在元类
中.
关于成员变量
的探索:
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100008208 LGPerson
(lldb) p (class_data_bits_t*)0x0000000100008228
(class_data_bits_t *) $1 = 0x0000000100008228
(lldb) p $1->data()
(class_rw_t *) $2 = 0x00000001018905b0
(lldb) p $2->ro()
(const class_ro_t *) $3 = 0x00000001000080a0
(lldb) p *$3
(const class_ro_t) $4 = {
flags = 388
instanceStart = 8
instanceSize = 24
reserved = 0
ivarLayout = 0x0000000100003f83 “\x02”
name = 0x0000000100003f7a “LGPerson”
baseMethodList = 0x00000001000080e8
baseProtocols = 0x0000000000000000
ivars = 0x0000000100008150
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100008198
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $4.ivars
(const ivar_list_t *const) $5 = 0x0000000100008150
(lldb) p *$5
(const ivar_list_t) $6 = {
entsize_list_tt<ivar_t, ivar_list_t, 0> = {
entsizeAndFlags = 32
count = 2
first = {
offset = 0x00000001000081d0
name = 0x0000000100003ef9 “hobby” //这里就是我们要找的成员变量
type = 0x0000000100003f8d “@“NSString””
alignment_raw = 3
size = 8
}
}
}
(lldb)
总结
:
属性
和实例方法
存在类
中成员变量
和类方法
存在元类
中
[面试题]
类对象内存存在几份?
我们来根据一段代码来验证一下:
//MARK: - 分析类对象内存存在个数
void lgTestClassNum(){
Class class1 = [LGPerson class];
Class class2 = [LGPerson alloc].class;
Class class3 = object_getClass([LGPerson alloc]);
Class class4 = [LGPerson alloc].class;
NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
}
看下打印结果:
发现这个几个class 的地址
都是一样的,说明类在内存里面只存在一份
。
objc_class 与 objc_object 有什么关系?
结构体类型objc_class
继承自objc_object
类型,其中objc_object
也是一个结构体,且有一个isa
属性,所以objc_class
也拥有了isa
属性
objc_object 与 对象的关系?
所有的对象
都来自NSObject
,但他们的底层都是来自objc_object
,所以对象
和objc_object
是继承关系