天天看點

iOS OC對象的本質窺探(對象分類)(二)

上面一篇文章講了OC對象的本質,編譯成C++對象是以什麼形式存儲的,一個對象占多少記憶體空間等問題,那麼在OC語言裡面,又分為幾種對象呢?其實平時的工作中通過[[NSObject alloc] init]這種形式建立的對象都是執行個體對象,另外還有兩類平時接觸甚少的對象,一個是類對象,一個就是元類對象。

開篇引題 類對象分為三種:

執行個體對象

類對象

元類對象

這三中類型的對象之間是什麼關系?每種類型的對象又有什麼特點呢?

開始一探究竟

  • 執行個體對象

    實作以下代碼

NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];        
NSLog(@"%p %p",
              object1,
              object2);

列印:0x100648340 0x100647440
           

總結:不多講了,第一篇文章已經寫的很詳細,

object1、object2是NSObject的instance對象(執行個體對象)

它們是不同的兩個對象,分别占據着兩塊不同的記憶體

instance對象在記憶體中存儲的資訊包括

isa指針

其他成員變量

存儲的是各個成員變量的值,還有一個isa指針,一個類在記憶體中的執行個體對象可以有多個。

記憶體存儲圖示

iOS OC對象的本質窺探(對象分類)(二)
  • 類對象

    1.擷取類對象

    兩種方法

NSObject *object1 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
           

還可以通過一個runtime函數

NSObject *object1 = [[NSObject alloc] init];        
Class objectClass3 = object_getClass(object1);
           

object_getClass 函數:傳入執行個體對象傳回類對象,傳入類對象,傳回元類對象

下面實作以下代碼

NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
        
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = object_getClass(object1);
Class objectClass4 = object_getClass(object2);
Class objectClass5 = [NSObject class];
        
NSLog(@"%p %p",
              object1,
              object2);
        
NSLog(@"%p %p %p %p %p",
              objectClass1,
              objectClass2,
              objectClass3,
              objectClass4,
              objectClass5);

列印:0x100648340 0x100647440
     0x7fff9dab4118 0x7fff9dab4118 0x7fff9dab4118 0x7fff9dab4118 0x7fff9dab4118
           

會發現兩個執行個體對象的記憶體位址是不一樣的,而通過兩個執行個體對象獲得的5個類對象的指針都是一樣的,說明,一個類在記憶體中隻有一個類對象。

先總結:

objectClass1 ~ objectClass5都是NSObject的class對象(類對象)

它們是同一個對象。每個類在記憶體中有且隻有一個class對象

class對象在記憶體中存儲的資訊主要包括

isa指針

superclass指針

類的屬性資訊(@property)、類的對象方法資訊(instance method)

類的協定資訊(protocol)、類的成員變量資訊(ivar)

記憶體存儲圖示

iOS OC對象的本質窺探(對象分類)(二)
  • 元類對象

    執行以下代碼擷取元類對象

// object_getClass  函數:傳入執行個體對象傳回類對象,傳入類對象,傳回元類對象
Class objectClass3 = object_getClass([NSObject class]);
           

總結:

objectMetaClass是NSObject的meta-class對象(元類對象)

每個類在記憶體中有且隻有一個meta-class對象

meta-class對象和class對象的記憶體結構是一樣的,但是用途不一樣,在記憶體中存儲的資訊主要包括

isa指針

superclass指針

類的類方法資訊(class method)

iOS OC對象的本質窺探(對象分類)(二)

關于如何證明剛剛所總結的三種類型的對象分别存儲的什麼資訊,通過閱讀蘋果源碼可知

  • 簡單圖示
    iOS OC對象的本質窺探(對象分類)(二)
    想要更深層次的了解類的本質還是需要自己去閱讀源碼
分析:執行個體對象,類對象和元類對象中都有isa指針,而類對象和元類對象中除了含有isa指針之外還有superclass指針,問題:isa指針和superclass指針分别有什麼作用呢?
  • 分析 建立一個Person類給person類分别聲明一個對象方法和一個類方法
// Person
@interface Person : NSObject <NSCopying>
{
    @public
    int _age;
}
@property (nonatomic, assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end

@implementation Person
- (void)personInstanceMethod{
}
+ (void)personClassMethod{
}
@end

// 調用 
Person *person = [[Person alloc] init];
[person personInstanceMethod];
[Person personClassMethod];
           
  • isa指針作用

    OC語言是消息機制 當執行[person personInstanceMethod]; 這行代碼時 實際會被編譯成 objc_msgSend(person, @selector(personInstanceMethod))進行調用,那麼剛剛說到執行個體對象中不存儲方法,那麼當調用時這個對象方法是怎麼獲得的呢?

    答案: 當執行個體對象調用對象方法時是通過isa指針,指向自己的類對象,找到類對象中的方法資訊,進行調用。同理,當執行 [Person personClassMethod]; 這行代碼時,執行個體對象通過isa指針指向自己的類對象,又通過類對象中的isa指針指向元類對象,擷取到類方法資訊。

    圖示:
    iOS OC對象的本質窺探(對象分類)(二)
  • 建立一個Person類給person類分别聲明一個對象方法和一個類方法,再建立一個繼承于Person類的Student類給Student類分别聲明一個對象方法和一個類方法
// Person
@interface Person : NSObject <NSCopying>
{
    @public
    int _age;
}
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end

@implementation Person

- (void)personInstanceMethod
{
    
}
+ (void)personClassMethod
{
    
}
@end

// Student
@interface Student : Person <NSCoding>
{
@public
    int _weight;
}
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end

@implementation MJStudent
- (void)studentInstanceMethod
{
    
}
+ (void)studentClassMethod
{
    
}
@end

// 調用
Student *student = [[Student alloc] init];
[student personInstanceMethod];
[Student personClassMethod];     
           
  • superclass指針作用

    總結:當執行[student personInstanceMethod]這行代碼時 student對象通過isa指針找到自己類對象,結果發現類對象中沒有personInstanceMethod這個方法,然後利用superclass指針找到父類也就是Person的類對象,檢視是否有 personInstanceMethod 方法資訊,如果有 就調用,沒有的話繼續向上查找。同理:當執行[Student personClassMethod]; 這行代碼時,student對象通過isa指針找到自己類對象,又通過類對象的isa指針找到自己的元類對象結果發現元類對象中沒有personClassMethod這個方法,元類對象利用superclass指針向上逐級尋找父類的元類對象是否有此方法。直到NSobject停止,如果找到就調用,找不到就會奔潰 unrecognized selector sent to class 0x1000011a0'

圖示:

iOS OC對象的本質窺探(對象分類)(二)

最後總結:

#####instance -> 執行個體對象,class -> 類對象,meta-class -> 元類對象

1.instance的isa指向class

2.class的isa指向meta-class

3.meta-class的isa指向基類的meta-class

4.class的superclass指向父類的class

5.如果沒有父類,superclass指針為nil

6.meta-class的superclass指向父類的meta-class

7.基類的meta-class的superclass指向基類的class

8.instance調用對象方法的軌迹

9.isa找到class,方法不存在,就通過superclass找父類

10.class調用類方法的軌迹

11.isa找meta-class,方法不存在,就通過superclass找父類

圖示:

iOS OC對象的本質窺探(對象分類)(二)
  • 畫一下[student personInstanceMethod]這行代碼的執行順序
    iOS OC對象的本質窺探(對象分類)(二)
  • 畫一下[Student load]這行代碼的執行順序
    iOS OC對象的本質窺探(對象分類)(二)
疑問???類方法再調用時先去自己的元類對象中尋找,如果沒有就去父類的元類對象中尋找,在沒有就去NSObject的元類對象中尋找,再就去NSObject的類對象中尋找(根據最上面的線可以看來)那是這麼回事嗎?下面來驗證一下
//建立一個Car類,繼承自NSObject
@interface Car : NSObject

@end

@implementation Car

@end

// 建立一個NSObject的類别
//.h中代碼
+ (void)test;
//.m中代碼
+ (void)test{
    NSLog(@"+[NSObject test] - %p", self);
}
//調用代碼
[Car test];

輸出 :[Car class] - 0x100001220
           

别的代碼都不變,将類别中.m中的代碼改成

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

輸出 :[Car class] - 0x100001220
           

再調用發現還是可以成功,具體的原因吧,我也說的不是很好,就不誤導别人了,如果有大神知曉,歡迎指正

如有疑問歡迎指正~

強烈推薦:

iOS擷取手機唯一标示

iOS 高德地圖實作大頭針展示,分級大頭針,自定制大頭針,在地圖上畫線,線和點共存,路線規劃(駕車路線規劃),路線導航,等一些常見的使用場景

如有疑問請看:iOS OC對象的本質窺探(一)