OC對象的本質及分類

Object-C的底層都是經過C/C++來實現的,因此OC中的對象也會轉化成C/C++中的某一個數據結構,c++

咱們在終端裏經過指令數據結構

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main_arm64.cpp

將oc代碼轉化爲c++代碼,咱們能夠看到NSObject的底層結構是:iphone

struct NSObject_IMPL {
    Class isa;
};

Class是一個指向對象的結構體指針函數

typedef struct objc_class *Class;

 

因此NSObject最終會轉化成一個結構體,內部只有一個指向對象的結構體指針ui

因此NSObject對象只會使用8個字節的內存空間來存儲指針(固然 實際上給它分配了16個內存空間)spa

NSLog(@"%zd",class_getInstanceSize([NSObject class])); //實例對象的成員所佔用的大小8 (實際使用的) 

NSLog(@"%zd",malloc_size((__bridge const void *)(obj))); //整個結構體佔用的是16(實際分配的)
同時,經過閱讀源碼咱們得知,當建立的對象分配的內存空間小於16個字節的時候  系統都會分配16個字節的空間  這屬因而蘋果規定。
    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;
    }

  

若是有一個student類繼承了object而且有倆個int屬性,那麼student所佔用的內存是多少呢?.net

student實際佔用內存爲16字節,系統分配的內存也是16字節。3d

 

假設有個person繼承NSObject,student繼承person,那麼person和student各佔用多少內存呢?指針

 

最終經過打印咱們發現,person,實際佔用16,系統分配16,student實際佔用16,系統分配16.code

爲何?person實際佔用16??int 4個字節 isa 8個字節 應該是12個字節啊?這就涉及到了前面寫到的結構體內存對齊了。

 

對象的分類

oc中的對象主要能夠分爲三類:

1.實例對象,就是經過類alloc出來的對象,每次調用alloc都會生成一個新的實例對象

 

object1 和 object2 就是兩個實例對象

實例對象在內存中存儲的信息包括:

一、isa指針 (其實isa也算是對象的成員變量  也就是說實例對象內部只包含本身的成員變量)

二、其餘成員變量(這裏是存儲成員變量的具體值)

 

 

二、類對象(class) 

objectClass1-5 都是NSObject的類對象 ,由於每一個類在內存中有且只有一個類對象  因此上面五個類對象實際上是同一個對象

類對象在內存中存儲的信息主要有:

一、isa指針

二、superclass指針

三、成員變量(這裏的成員變量只是描述性的  好比有哪些變量 是什麼類型的  並非實例對象的具體變量值) 

四、類的對象方法(-號開頭的方法)

五、類的協議信息和屬性信息

類對象的本質結構↓↓↓

 

三、元類對象(meta-class)

objectMetaClass就是NSObject的元類對象,元類對象也是每一個類在內存中有且只有一個,元類對象和類對象在結構上很是類似。

元類對象在內存中春初的主要信息有:

一、isa指針

二、superclass指針

三、類方法(+號開頭的方法) 

 

咱們看到經過object_getclass方法即能得到元類對象 也能得到類對象  經過查看源碼咱們能夠得知object_getclass會判斷傳進來的參數是類對象仍是實例對象  若是是實例對象則返回類對象  若是傳進來的是類對象則返回元類對象

咱們也能夠經過下面的函數來判斷對象是否是元類對象

 

也就是說經過alloc建立的是實例對象  經過object_getclass(類對象)建立的是元類對象  其餘對象則是類對象  可是類對象和元類對象有且只有一個

 

三類對象中 都含有isa指針,那麼這個isa指針指向什麼?

實例對象的isa指向類對象  類對象的isa指向元類對象  元類對象的isa指向基類的元類對象

正是經過isa指針 才讓三種對象產生關聯

好比說,一個實例對象想調用對象方法  可是對象方法存放在類對象中  那麼就是經過isa找到對象方法再進行調用   

同理  當調用類方法的時候  類方法是存放在元類對象中的  類對象經過isa指針找到元類對象 讀取類方法列表中的類方法進行調用

 

superclass指針

在類對象和元類對象中都有一個superclass指針,其實這兩種對象中的superclass指針做用相似,都是指向父類對象

類對象中的superclass指針:

好比如今有一個Person對象繼承自NSObject  有一個Student繼承自Person  當studen的實例對象調用對象方法的時候,首先實例對象會根據本身的isa指針去類對象中找有沒有對應的方法  沒有的話類對象會根據本身的superclass指針去父類的類對象中去查找(也就是student的類對象根據superclass指針去Person的類對象中去查找有沒有對應的對象方法  再沒有的話Person的類對象會根據本身的superclass指針去NSObject的類對象中去尋找 尋找到基類在沒有對應方法的話就會報方法找不到的錯誤)

 

而元類對象中的superclass指針也是指引類對象去父類對象中尋找對應的類方法:

按照上面的例子,Student這個類 想調用一個類方法,首先是Student的類對象 根據isa指針去Student的元類對象中查找有沒有對應的類方法  沒有的話Student的元類對象會根據本身的superclass指針去父類的元類對象(也就是Person的元類對象)中查找有沒有對應的類方法,在沒有的話Person的元類對象再根據本身的superclass指針去NSObject的元類對象中尋找 有的話進行調用 沒有的話NSObject的元類對象會根據superclass指針去NSObject的類對象中去尋找是否有相同名稱的對象方法(這個地方下面會具體講到爲何基類的superclass指針會指向對應的類對象)

 

 

 

 

關於上面提到的爲何基類的superclass指針爲何在找不到方法的時候會指向基類的類對象 也就是爲何沒有找到對應的類方法的狀況下卻能夠調用同名對象方法?

關於這一點咱們經過代碼來驗證:

首先咱們新建一個NSObject的分類,在.h文件中聲明一個test的類方法,但在.m文件中並未實現這個類方法  而是實現了同名的對象方法()

#import "NSObject+Test.h"

@implementation NSObject (Test)

//+ (void)test
//{
//    NSLog(@"+[NSObject test] - %p", self);
//}

- (void)test
{
    NSLog(@"-[NSObject test] - %p", self);
}

@end

咱們調用類方法發現,及時沒有對應的類方法,程序也能夠正常運行,而且成功調用了同名的對象方法:

 

假如咱們在m文件沒有實現同名test的對象方法,那麼程序會報錯的:

+[NSObject test]: unrecognized selector sent to class 0x7fffaddd7140

 

關於在h文件中有類方法的聲明,這個是沒有影響的 由於沒有這個聲明的話程序根本跑不起來  咱們關注的點是基類的superclass指針爲何在找不到方法的時候會指向基類的類對象尋找同名的對象方法  

好比咱們在h文件中聲明瞭test的對象方法  m文件沒有實現test方法 一樣會報unrecognized錯  這就是由於基類的對象方法中找不到方法後直接返回空值  而不是像類方法同樣從元類對象找不到再去到類對象找同名對象方法

 

 

關於基類的superclass指針爲何在找不到方法的時候會指向基類的類對象,這是由於oc在調用方法的時候其實是轉換爲c/c++去底層實現的  可是c/c++的底層實現並無區分類方法仍是對象方法 也就是沒有區分+-號

好比

[NSObject test];

其實是轉換爲了

objc_msgSend([NSObject class], @selector(test))

沒有區分+-號  因此在基類元類對象沒有找到對應的類方法後回去基類的類對象中查看是否有同名的對象方法  有的話就調用  再沒有的話就報錯了

 

 

 

 

 

 

 

NSObject本質揭祕

查看OC對象佔用字節數

相關文章
相關標籤/搜索