該內容僅供自己學習記錄,前前後後也積累了好多有關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中對象的研究,首先我們得搞清楚對象的分類
- 實例對象
instance class
- 類對象
class
- 元類對象
meta-class
爲了方便理解,我們舉個例子
有Studen
學生類,Person
人類,NSObject
猿人類,首先我們將問題具象化,學生當然屬於人類,人類有屬於猿人類,繼承關係我這裏不上代碼,比較懶,Student -> Person -> NSObject
簡單粗暴的上結構圖,首先我說明這些圖是來自小馬哥教育課件裏的,我覺得小馬哥做的都清晰明瞭
現有:Student *xiaoming = [[Student alloc] init];
那麼xiaoming
是Student
創建的一個實例對象,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
class
的isa
指向meta-class
也就是Student
的isa
是指向Student
的元類的meta-class
的isa
指向基類的meta-class
,也就是說,Student
的元類的isa
是指向NSObject
的元類的
接下來分析superclass
指針的指向
class
的superclass
指向父類的class
在這裏就是Student
的superclass
指向的是Person類
meta-class
的superclass
指向父類的meta-class
- 基類的
meta-class
的superclass
指向基類的class
然後我們分析下當xiaoming
要調用自己的實例方法時的軌跡
- 先通過
xiaoming
的類的isa
指針找到Student
,然後在Student
實例方法列表中尋找實例方法,找到的話就調用,找不到的話,通過superclass
在父類Person
中去查找,找不到的話,在通過Person
中的superclass
在Person
的父類中查找
最後我們分析下Student
如何要調用類方法的軌跡
- 先通過
Student
類中的isa
指正找到Student
的meta-class
,從meta-class
中查找類方法,找不到的話,就通過superclass
在元類的父類裏面去找(按照上圖已經找到NSObject
的meta-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 = 元類對象的地址