你們好,我是葉孤城,和我很是尊敬的大神同名。前一篇文章中,咱們知道了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