iOS底層學習 - 類的前世此生(一)

經過上一章節的學習,咱們已經瞭解了對象的底層實現,那麼類,NSObject在底層是以什麼方式存在的呢,屬性,方法,協議,都是怎麼存在於類裏面的,這一章節就來深刻了解一下數組

傳送門☞iOS底層探索-準備工做bash

傳送門☞iOS底層學習-OC對象前世此生app

準備工做

建立以下類post

@interface LGPerson : NSObject{
    NSString *hobby;
}

@property (nonatomic, copy) NSString *nickName;

- (void)sayHello;
+ (void)sayHappy;

@end
複製代碼
@implementation LGPerson

- (void)sayHello{
    NSLog(@"LGPerson say : Hello!!!");
}

+ (void)sayHappy{
    NSLog(@"LGPerson say : Happy!!!");
}


@end
複製代碼

指針偏移

int a = 10; 
int b = 10; 
LGNSLog(@"%d -- %p",a,&a);
LGNSLog(@"%d -- %p",b,&b);   

輸出:
KC打印: 10 -- 0x7ffeefbff4fc
KC打印: 10 -- 0x7ffeefbff4f8
複製代碼
LGPerson *p1 = [LGPerson alloc];
LGPerson *p2 = [LGPerson alloc];
LGNSLog(@"%@ -- %p",p1,&p1);
LGNSLog(@"%@ -- %p",p2,&p2);    

輸出:
KC打印: <LGPerson: 0x100544c30> -- 0x7ffeefbff4f8
KC打印: <LGPerson: 0x100545a20> -- 0x7ffeefbff4f0
複製代碼

經過以上兩個例子,能夠得出學習

  • 常量的是值拷貝,只是對值就好了賦值,地址不一樣
  • 對象的是引用拷貝,各自開闢了內存空間,對象的地址不一樣,對象地址的指針地址也不相同

// 數組指針
    int c[4] = {1,2,3,4};
    int *d   = c;
    NSLog(@"%p - %p - %p",&c,&c[0],&c[1]);
    NSLog(@"%p - %p - %p",d,d+1,d+2);

    for (int i = 0; i<4; i++) {
    // int value = c[i];
    int value = *(d+i);
    LGNSLog(@"%d",value);
    }
    
    NSLog(@"指針 - 內存偏移");
複製代碼

經過上述的例子,能夠獲得

  • 一個對象的地址就是首個元素的地址
  • 經過對指針的偏移,能夠獲得相對應的元素值

類的底層實現

類的實現實在編譯期就申請好內存了,因此咱們須要能夠在編譯期查看類的底層實現 編譯後經過MackOView能夠看到,LGPerson類已經生成ui

LGPerson *person = [LGPerson alloc];
Class pClass     = object_getClass(person);
複製代碼

經過clang命令編譯main文件後獲得cpp文件,能夠發現,Class的底層結構是objc_class,裏面有一個廢棄的isa指針,因此不作研究.atom

在objc源碼中咱們能夠發現, objc_class結構以下,是繼承自 objc_object,因此其實類也是一種對象,所謂 萬物皆對象

其中Class中ISA對象是在 objc_object定義中,NSObject是OC對objc_object的仿寫,結構是同樣的

咱們能夠看到objc_class成員變量主要有一下四個spa

  • Class ISA:主要關聯類
  • Class superclass:父類
  • cache_t cache
  • class_data_bits_t bits

cache_t cache

經過源碼咱們可得 cache_t cache爲一個結構體,所佔字節數也如圖所示,共16字節,不足以存放類中的屬性和方法,因此,屬性方法等數據,必定存儲在 class_data_bits_t bits結構中

class_data_bits_t bits

屬性方法等數據是存儲在class_data_bits_t bits中,而且能夠看到class_rw_t中的數據都是來自於此。且結構中存在屬性,方法,協議列表等,所以遇到跟蹤class_data_bits_t bits,能夠經過指針偏移來獲取到class_data_bits_t bits中的數據,經過結構可知,在首地址偏移32字節便可獲得3d

咱們能夠經過LLDB命令和指針偏移,一步步來探索指針

首先打印出類的16進制存儲

接着將首地址 0x1000023b0偏移32字節,在16進制下也就是 0x20,獲得 0x00000001000023d0,因爲 class_data_bits_t bits不是對象類型,因此須要強轉一下,獲得以下地址

繼續,咱們想獲得 class_rw_t結構的data,那麼調用方法可獲得 class_rw_t中的內容以下

屬性變量存儲

經過上面打印出的class_rw_t中,咱們能夠看到,根據名稱,屬性應該是存儲在properties中,經過命令打印可得下圖

根據 list_array_tt結構可得此爲二維數組,且根據結構可得數據應該存儲在 list結構中
繼續打印能夠 property_list_t,可得其繼承自 entsize_list_tt,打印其中的元素 Element first;能夠獲得保存的類中保存的屬性

成員變量存儲

在上述探尋屬性的過程當中,咱們並無看到成員變量的保存,因此,成員變量不是保存在properties中,應該是存在其餘的結構中,根據class_rw_t結構思考可得,ro中存儲成員變量的可能性較大,因此按照上述流程探尋ro

到這裏,咱們發現了 const class_ro_t中存在一個 ivars,成員變量應該在此
打印 ivars,發現其中first中已經有了咱們的屬性hobby,驗證成功
可是經過 count咱們發現爲2,因此應該還有一個成員變量,咱們能夠打印發現是 _nickName,因此說明 屬性會自動生成對應的下劃線成員變量

實例方法存儲

經過對class_rw_t研究,咱們發現有一個methods元素,那麼這個頗有可能就是存放方法的列表,經過打印咱們可得

此結構和屬性的相似,因此咱們接着打印 list,能夠發現咱們的實例方法 sayHello,此時已經驗證了方法的存儲

同理, count爲4,咱們看一下剩下的是什麼方法。咱們發現是屬性的get和set方法已經默認的析構方法,驗證了 屬性會自動建立get和set

類方法存儲

經過上面的存儲探究,咱們發現並無類對象中並無類方法的存儲,且類繼承子NSObject,且NSObject中並無對應的類方法,因此,咱們能夠猜想,類方法是存在於元類之中的,經過類對象的isa能夠找到元類

經過對元類結構進行上述LLDB探索,能夠清楚的發現類方法存在於其中

總結

經過上面的一系列探索,咱們可得以下結論

  • 萬物皆對象,均繼承自objc_object
  • 屬性和成員變量都存放在類的class_rw_t結構體中
  • 屬性會自動生成get和set方法,成員變量不會
  • 實例方法會存在於類中,而類方法會存在於元類之中,且是以實例方法的形式存在的
相關文章
相關標籤/搜索