iOS Object-C中類對象的本質

該內容僅供自己學習記錄,前前後後也積累了好多有關Objective-C的東西,今天偶然看到一個有關MJ的視頻,特此記錄一下

一個NSObject對象佔用多少內存字節
  • 首先回答這個問題,要分爲兩部分
    因爲對象本質是個結構體,裏面有isa指針指向自己所屬的類(類的isa指針指向元類,元類的isa指針指向基類的meta-class對象),總歸是個指針,那麼,它佔用的自己數是8(64bit位環境下)個字節沒錯了,所以通過class_getInstanceSize這個函數打印出來的是8個字節
  • 但是系統會對一個NSObject對象分配16個字節,通過malloc_size函數就可以打印出來

解析:
那我們接下來就看看這兩個函數內部都是怎麼調用的
首先我們來看 class_getInstanceSize這個函數的源碼,源碼在這裏下載

size_t class_getInstanceSize(Class cls)
{
    if (!cls) return 0;
    return cls->alignedInstanceSize();
}
.....
// Class's ivar size rounded up to a pointer-size boundary.
uint32_t alignedInstanceSize() {
    return word_align(unalignedInstanceSize());
}

從這裏我們就可以看出,本質上是調用alignedInstanceSize(),該函數描述爲返回Class的ivar的大小,因爲一個Class只有一個isa指針,故返回8

然後我們來看malloc_size,首先,該函數是什麼時候調用的呢?當我們初始化一個實例對象的時候,例如NSObject *obj = [[NSObject alloc] init];其實本質上並不是調用的alloc()方法,本質上是調用的allocWithZone,對allocWithZone方法進行跟蹤後發現

id objc_alloc(Class cls) 
{
    return callAlloc(cls,true/*checkNil*/,ture/*allocwithZone*/)
}
id objc_allocWithZone(Class cls) 
{
    return callAlloc(cls,true/*checkNil*/,ture/*allocwithZone*/)
}
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
     .....
     id obj = class_createInstance(cls,0);
}
id class_createInstance(Class cls, size_t extraBytes)
{
    return _class_createInstanceFromZone(cls, extraBytes,nil)

}
....
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct = true, size_t *outAllocatedSize = nil) 
{
     ....
     size_t size = cls->instanceSize(extraBytes)//分配的大小
}

size_t instanceSize(size_t extraBytes) {
     size_t size = alignedInstanceSize() + extraBytes;
     //CF requires all objects be at least 16 bytes.
     if (size < 16) size = 16;
     return size;
}

上述代碼省略了非關注的問題,並且調整了順序,當然,這些代碼在源碼以及編譯成c/c++文件後都可以找到從,這裏我們就能找到真正的原因,CoreFoundation要求所有的objects最少爲16bit

對象的本質

接下來我們就要進入對OC中對象的研究,首先我們得搞清楚對象的分類

  1. 實例對象 instance class
  2. 類對象 class
  3. 元類對象 meta-class

爲了方便理解,我們舉個例子

Studen 學生類,Person人類,NSObject猿人類,首先我們將問題具象化,學生當然屬於人類,人類有屬於猿人類,繼承關係我這裏不上代碼,比較懶,Student -> Person -> NSObject
簡單粗暴的上結構圖,首先我說明這些圖是來自小馬哥教育課件裏的,我覺得小馬哥做的都清晰明瞭
現有:Student *xiaoming = [[Student alloc] init];
那麼xiaomingStudent創建的一個實例對象,xiaoming的內部結構爲

xiaoming類
假設我們Student就已經有age這個屬性了,上圖就是xiaoming這個類裏面存放的東西
那麼Student裏面又有什麼呢?

元類對象裏面放的什麼信息呢
在這裏插入圖片描述
稍微解釋下:
instance圖裏是xiaoming實例類的信息,以及存儲的Student屬性的值
class圖是Student類信息的圖,裏面存放isa指針,superclass指針,屬性信息、實例方法、協議信息,成員變量信息…
meta-class圖是Student類的元類對象,存放isa指針,superclass指針,類方法信息…,下面要上一張重要的圖,解釋下isa,superClass這兩個比較抽象的東西
在這裏插入圖片描述
這張圖總結的比較好:
首先我們分析isa指針的指向(以Student爲例,同理可分析Person,NSObject)

  • instance of Subclass其實就是指上面舉的例子xiaoming,它的isa指向class,也就是上面舉得例子Student
  • classisa指向meta-class 也就是Studentisa是指向Student的元類的
  • meta-classisa指向基類的meta-class,也就是說,Student的元類的isa是指向NSObject的元類的

接下來分析superclass指針的指向

  • classsuperclass指向父類的class在這裏就是Studentsuperclass指向的是Person類
  • meta-classsuperclass指向父類的meta-class
  • 基類的meta-classsuperclass指向基類的class

然後我們分析下當xiaoming要調用自己的實例方法時的軌跡

  • 先通過xiaoming的類的isa指針找到Student,然後在Student實例方法列表中尋找實例方法,找到的話就調用,找不到的話,通過superclass在父類Person中去查找,找不到的話,在通過Person中的superclassPerson的父類中查找

最後我們分析下Student如何要調用類方法的軌跡

  • 先通過Student類中的isa指正找到Studentmeta-class,從meta-class中查找類方法,找不到的話,就通過superclass在元類的父類裏面去找(按照上圖已經找到NSObjectmeta-class),找不到的話,就返回到NSObject類中去查找方法(就是上圖中Root class (meta) 又有箭頭指向 Root class (class))
objc_class內部結構
struct objc_class {
    Class isa;
    Class superclass;
    cache_t cache;//方法緩存
    class_data_bits_t bits; //用於緩存具體類信息
};

bits裏的內容如下

struct class_rw_t {
    uint32_5 flags;
    uint32_t version;
    const class_ro_t *ro;
    method_list_t *methods;//方法列表
    property_list_t *properties;//屬性列表
    const protocol_list_t * protocols;//協議列表
    Class fistSubclass;
    Class nextSiblingClass;
    char *demangledName;
}

const class_ro_t *ro;ro中具體的結構

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    unit32_t instanceSize;//instance對象佔用的內存空間
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout
    const char *name; //類名
    method_list_t *baseMethodList;
    protocol_list_t *baseProtocols;
    const ivar_list_t *ivars; //成員變量列表
    const uint8_t weakIvarLayout;
    property_list_t *baseProperties;
};

上述結構爲最新的objc源碼中的結構,xcode裏面的太舊了,早已經廢棄

objc_class內部結構

問題:isa指針的地址 ?= ?元類對象的地址嗎?
答案是否定的
正確答案: isa & ISA_MASK = 元類對象的地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章