你们好,我是叶孤城,和我很是尊敬的大神同名。前一篇文章中,咱们知道了isa的存在、做用,以及类的底层本质是一个objc_object的结构体。可是也带出了一个新的词儿—— 元类。今儿个咱们就谈谈元类,上一篇文章中,那个经典的类结构图是怎么画出来的。markdown
没错,就是上面这幅图。app
解释元类以前,先说一个咱们熟悉的不能再熟悉的东西: 对象和类。 As we know , 对象是由类产生(准确的说:对象由类对象实例化),咱们前面说过:类也是一个objc_object,叫类对象。那么 类对象 同理也是由一个 "类" 产生,那么这个"类"就是 元类 。spa
当咱们再回顾这条线: obj -> LYPerson(类对象) ->LYPerson(元类) -> NSObject(根元类) -> NSObject(根根元类) 这就理解了为何有元类,元类是干什么的,其实就是产生类对象的类。3d
void lgTestNSObject(){
// Student实例对象
Student *object1 = [[Student alloc] init];
// Student类
Class class1 = object_getClass(object1);
// Student元类
Class metaClass = object_getClass(class1);
// NSObject 根元类
Class rootMetaClass = object_getClass(metaClass);
// NSObject 根根元类
Class rootRootMetaClass = object_getClass(rootMetaClass);
NSLog(@"\n%p 实例对象 \n%p 类 %@\n%p 元类 %@\n%p 根元类 %@\n%p 根根元类 %@",object1,class1,NSStringFromClass(class1),metaClass,NSStringFromClass(metaClass),rootMetaClass,NSStringFromClass(rootMetaClass),rootRootMetaClass,NSStringFromClass(rootRootMetaClass));
NSLog(@"=> %p",object_getClass(rootRootMetaClass));
}
复制代码
总结:指针
如下若有说到类对象,其实就是类。调试
咱们开发中,一个类能够alloc N个obj,类对象不是,在app的生命周期内,内存中只存在一份。咱们用两种方式作个实验:code
void lgTestClassNum(){
Class class1 = [LYPerson class];
Class class2 = [LYPerson alloc].class;
Class class3 = object_getClass([LYPerson alloc]);
NSLog(@"\n[LGPerson class]\t\t\t\t\t%p-\n[LGPerson alloc].class\t\t\t\t%p-\nobject_getClass([LGPerson alloc])\t%p-\n",class1,class2,class3);
}
复制代码
演示的object、class是我代码里的断点处本身定义的orm
// 查看变量object的内存地址,并产生一个lldb中的变量$0,代替object
(lldb) p object // 这是一个对象
(Student *) $0 = 0x000000010061eb80
复制代码
// 也是查看内容
(lldb) po object // 这是一个对象
<Student: 0x10061eb80>
(lldb) po class // 这是一个Class类型
复制代码
Student (lldb) po a // 这是一个int变量a 10对象
* ```
// 转16进制
(lldb) p/x object // object的内容是地址
(Student *) $2 = 0x000000010061eb80
(lldb) p/x 10 // 10是一个十进制的数
(int) $3 = 0x0000000a
// 转2进制
(lldb) p/t object
(Student *) $3 = 0b0000000000000000000000000000000100000000011000011110101110000000
复制代码
// x 查看内存
(lldb) x object
0x100512ce0: 65 33 00 00 01 80 1d 00 00 00 00 00 00 00 00 00 e3..............
0x100512cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
复制代码
// x/[d]gx 查看内存分布,d表示查看几块,一块是8字节
(lldb) x/gx object
0x100512ce0: 0x001d800100003365
(lldb) x/4gx object
0x100512ce0: 0x001d800100003365 0x0000000000000000
0x100512cf0: 0x0000000000000000 0x0000000000000000
(lldb) x/8gx object
0x100512ce0: 0x001d800100003365 0x0000000000000000
0x100512cf0: 0x0000000000000000 0x0000000000000000
0x100512d00: 0x0000000000000000 0x1000000010050b0e
0x100512d10: 0x0000000000000000 0x0000000000000000
复制代码
// 咱们不只能够查看变量,也能够直接查看内存地址
(lldb) po object
<Student: 0x10046dba0>
// 根据得到的内存地址,查看内存分布
(lldb) x/4gx 0x10046dba0
0x10046dba0: 0x001d800100003365 0x0000000000000000
0x10046dbb0: 0x0000000000000000 0x0000000000000000
复制代码
0x10046dba0就是首地址,内存分布也是从首地址开始分配继承
结论:内存地址+内容都是LYPerson,OK,没有疑问了,类对象只有一个。
好,问题来了
类对象只有一个,类对象的类元类确定也只有一个。
LYPerson 和 LYAnimal 均是继承NSObject的自定义类,class一、class2分别是他们的元类,class十一、class22则分别是他们的根元类,也就是他们元类的类,都是NSOBject,内存地址也是同样的,
这个咱们使用LLDB类查看,OC代码每次只能运行一次,咱们的LLDB能够尽情的“追根溯源”
咱们从根元类开始看
一、根元类NSObject 后面的根根元类仍是NSObject ,内存地址同样;
二、根根元类NSObject 后面的元类仍是本身,内存地址同样;
因此咱们知道:
根据目前咱们掌握的知识,咱们作一个isa画像,对,仍是前一篇文章末尾的那个图
前面咱们一直在讲直接继承于NSObject的子类,若是LYPerson又派生了Student类呢?
同理,咱们能够验证一番:
准备一个继承于LYPerson的子类Student
#import "LYPerson.h"
NS_ASSUME_NONNULL_BEGIN
@interface Student : LYPerson
@end
NS_ASSUME_NONNULL_END
复制代码
仍是上面的那个isa指向链图:
咱们分析过runtime源码:isa是一个Class类型,都是Class是一个objc_class的结构体指针。
typedef struct objc_class *Class;
struct objc_class : objc_object {
// Class ISA; // isa是从objc_object继承而来,因此objc_class与生自带isa
Class superclass; // 重点在这:这个就是标记继承于哪一个类。Student : LYPerson : NSObject
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
.
.
.
.
}
复制代码
因此Class的isa指向的类对象、元类、根元类都是objc_class;因此说,从类对象开始,不光有isa这条线,还有一般所认识的“继承”着一条主线。类就是个双主演的电影:isa和superClass。
因此这张图咱们又变了一下,加上“继承”这条主线:用实线表示继承,由于NSOBject在内存中只有一份,因此咱们只保留一个NSObject
这张图仍是残缺的,没有NSObject。NSObject是根类,根类上面没的继承了,是nil
而且,NSObject类对象和他的元类不是同一个
NSObject的元类已是根元类!,因此上面的图,咱们整理一下,去掉标记的person、student,将NSObject元类合并,整理后以下图所示:
事情并无结束,根元类NSObject是个objc_class,他的superClass指向了哪里?
看完图确定知道他指向的是根类NSObject,此处伪装不知道,咱们来找一找。
祭上lldb
咱们经过x/4gx
查看根元类的内存分布,第8到第16个字节是superclass
。superClass
就是NSObject类对象
,内存地址都是0x0000000100334140
因此,咱们最终得出了这么一个类的isa和继承关系的走位图
0x0000000100334140
是superClass?首先 ,object_class是一个结构体,咱们以前专门谈过结构体的内存对齐,咱们知道,结构体的内存分配是按照成员顺序,superClass在结构体内的位置排在第二位,第一位是从objc_object继承的isa,isa占8个字节,superClass和isa同样,也占8个字节,因此,咱们从第二个内存快
struct objc_class : objc_object {
// Class ISA; // 占8个字节, 0~7
Class superclass; // 占8个字节, 8 ~ 15
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
.
.
.
.
}
复制代码
因此,红色区域是superClass