類的本質-類對象


前言

今天整理了下自己電腦里的一些碎片筆記,時間有限只整理了這篇文章——類的本質,大家可以進行參考。

1.本質

  • 類的本質其實也是一個對象(類對象)
  • 程序中第一次使用該類的時候被創建,在整個程序中只有一份。
  • 此后每次使用都是這個類對象,它在程序運行時一直存在。
  • 類對象是一種數據結構,存儲類的基本信息:類大小,類名稱,類的版本,繼承層次,以及消息與函數的映射表等
  • 類對象代表類,Class類型,對象方法屬於類對象
  • 如果消息的接收者是類名,則類名代表類對象
  • 所有類的實例都由類對象生成,類對象會把實例的isa的值修改成自己的地址,每個實例的isa都指向該實例的類對象

2.如何獲取類對象

  • 通過實例對象

    格式:[實例對象 class]; 如: [dog class];
  • 通過類名獲取(類名其實就是類對象)

    格式:[類名 class]; 如:[Dog class]

3.類對象的用法

  • 用來調用類方法
[Dog test]; Class c = [Dog class]; [c test];
  • 用來創建實例對象
Dog *g = [Dog new]; Class c = [Dog class]; Dog *g1 = [c new];

4.類對象的存儲


存儲.png

5.OC實例對象、類對象、元數據、之間關系

  • Objective-C是一門面向對象的編程語言。

    • 每一個對象 都是一個類的實例。
    • 每一個對象 都有一個名為isa的指針,指向該對象的類。
    • 每一個類都描述了一系列它的實例的特點,包括成員變量的列表,成員函數的列表等。
    • 每一個對象都可以接受消息,而對象能夠接收的消息列表是保存在它所對應的類中。
  • 在XCode中按Shift + Command + O打開文件搜索框,然后輸入NSObject.h和objc.h,可以打開 NSObject的定義頭文件,通過頭文件我們可以看到,NSObject就是一個包含isa指針的結構體,如下圖所示:
NSObject.h @interface NSObject <NSObject> { Class isa OBJC_ISA_AVAILABILITY; }
objc.h /// An opaque type that represents an Objective-C class. typedef struct objc_class *Class; /// Represents an instance of a class. struct objc_object { Class isa OBJC_ISA_AVAILABILITY; };
  • 按照面向對象語言的設計原則,所有事物都應該是對象(嚴格來說 Objective-C並沒有完全做到這一點,因為它有象int,double這樣的簡單 變量類型)
    • 在Objective-C語言中,每一個類實際上也是一個對象。每一個類也有一個名為isa的指針。每一個類都可以接受消息,例如[NSObject new],就是向NSObject這個類發送名為new的消息。
    • 在XCode中按Shift + Command + O,然后輸入runtime.h,可以打開Class的定義頭文件,通過頭文件我們可以看到,Class也是一個包含isa指針的結構體,如下圖所示。(圖中除了isa外還有其它成員變量,但那是為了兼容非2.0版的Objective-C的遺留邏輯,大家可以忽略它。)
runtime.h struct objc_class { Class isa OBJC_ISA_AVAILABILITY; #if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE; const char *name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; struct objc_method_list **methodLists OBJC2_UNAVAILABLE; struct objc_cache *cache OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE;
  • 因為類也是一個對象,那它也必須是另一個類的實例,這個類就是元類 (metaclass)。

    • 元類保存了類方法的列表。當一個類方法被調用時,元類會首先查找它本身是否有該類方法的實現,如果沒有則該元類會向它的父類查找該方法,直到一直找到繼承鏈的頭。
    • 元類(metaclass)也是一個對象,那么元類的isa指針又指向哪里呢?為了設計上的完整,所有的元類的isa指針都會指向一個根元類(root metaclass)。
    • 根元類(root metaclass)本身的isa指針指向自己,這樣就行成了一個閉環。上面說􏰀到,一個對象能夠接收的消息列表是保存在它所對應的類中的。在實際編程中,我們幾乎不會遇到向元類發消息的情況,那它的isa 指針在實際上很少用到。不過這么設計保證了面向對象的干凈,即所有事物都是對象,都有isa指針。
    • 由於類方法的定義是保存在元類(metaclass)中,而方法調用的規則是,如果該類沒有一個方法的實現,則向它的父類繼續查找。所以為了保證父類的類方法可以在子類中可以被調用,所以子類的元類會繼承父類的元類,換而言之,類對象和元類對象有着同樣的繼承關系。
  • 下面這張圖或許能夠 讓大家對isa和繼承的關系清楚一些

其中:實線箭頭代表類的繼承關系,比如EOCStudent繼承自EOCPerson,也就是說,EOCStudent是EOCPerson的子類。就可以用實線表示這種繼承關系:EOCStudent —>EOCPerson。

虛線箭頭代表對象和類的從屬關系,比如一個對象student屬於EOCStudent類,也就是說,student是EOCStudent的實例。就可以用虛線表示這種從屬關系:student—>EOCStudent。

引用《Effective Objective-C 2.0 編寫高質量iOS與OS X代碼的52個有效方法》中的一段話:superclass指針確定了繼承關系,而isa指針描述了實例所屬的類。通過這張布局關系圖即可進行“類型信息查詢”。我們能查出對象是否能夠響應某個選擇子(selector),是否遵從某項協議,並且能夠看出某對象位於集成體系的哪一部分


繼承/從屬關系圖
  • 上圖中,最讓人困惑的莫過於Root Class了。在實現中,Root Class是指
  • NSObject,我們可以從圖中看出:
  • NSObject類對象包括它的對象實例方法。
  • NSObject的元對象包括它的類方法,例如new方法。
  • NSObject的元對象繼承自NSObject類。
  • 一個NSObject的類中的方法同時也會被NSObject的子類在查找方法時找到。

6.如何查詢類型信息

可以使用“類型信息查詢方法”來查詢類的繼承體系。其中,“isMemberOfClass:”可以判斷對象是否是特定類的實例。而”isKindOfClass:”可以判斷對象是否是某個類或者其派生子類的實例。而本質上,這兩個類型信息查詢方法是使用對象的isa指針獲取對象所屬的類(因為類對象也是對象,所以也有isa指針,該指針指向元類,也就是類對象所屬的類),然后通過類繼承體系中的superclass指針在繼承體系中游走。Objective-C與其他語言不同,Objective-C必須查詢類型信息,才能完全了解對象的真實類型。

另外,需要注意的是,我們從集合對象(collection)中獲取的對象,通常會用到這兩個查詢類型信息的方法。因為從集合對象中取出來的對象不是強類型的(strongly typed),其類型通常是id。回想一下,我們從一個數組中取出來的對象,其返回值是id類型的。這就是為什么我們可以在這個取出來的對象身上通過中括號”[ ]”的形式調用任何方法,卻不能通過點語法來調用方法。不過,為了安全起見,如果涉及到對集合對象中的某個對象進行操作,我們還是需要做一下類型判斷比較好。如下所示:

    for (id object in array) { if (object isKindOfClass:[NSString class]) { // object is an instance of NSString } }

當然,也可以用比較類對象是否等同的方法來判斷對象是否屬於某個類。若是如此,那就應該使用==操作符,而不要使用比較Objective-C對象使常用的“isEqual:”方法。因為==操作符比較的是指針是否相等,也就是比較內存地址是否相同。而"isEqual:"比較的是兩個Objective-C對象的值是否相等。此處用==操作符,原因在於,類對象類對象是“單例”,在應用程序范圍內,每個類的Class僅有一個實例,在整個內存中僅有一份(因為+(void)load方法和+ (void)initialize只被調用一次)。所以也可以用下面這種方進行比較:

if ([object class] == [EOCSomeClass class]) { // object is an instance of EOCSomeClass }

雖然調用class方法和isKindOfClass:方法都可以查詢一個對象的類型。但是還是建議使用后者。下面筆者引用《Effective Objective-C 2.0 編寫高質量iOS與OS X代碼的52個有效方法》中的一段話來進行解釋:

雖然使用"class方法"也可以查詢對象的類型信息。但是還是建議使用isKindOfClass:這樣的類型信息查詢方法。因為后者可以正確處理那些使用了消息傳遞機制對象。比方說某個對象可能會把其的所有選擇子(selector)都轉發給另一個對象(開啟了消息轉發功能)。這樣的對象叫做”代理(proxy)“,此種對象所屬的類均以NSProxy為根類(root class)。通常情況下,如果在此種代理對象上調用class方法,那么返回的是代理對象本身(NSProxy的子類),而非接受代理的對象所屬的類。然而,若是改用“isKindOfClass:”這樣的類型信息查詢方法,那么代理對象就會把這條消息轉給“接受代理的對象(proxy object)”。也就是說,這條消息(指isKindOfClass:)的返回值與直接接受代理的對象身上查詢其類型信息所得的結果相同。因此,這樣查出來的類對象與直接通過class方法所返回的那個類對象不同,class方法所返回類表示發起代理的對象,而非接受代理的對象

文/VV木公子(簡書作者)
PS:如非特別說明,所有文章均為原創作品,著作權歸作者所有,轉載轉載請聯系作者獲得授權,並注明出處,所有打賞均歸本人所有!

如果您是iOS開發者,或者對本篇文章感興趣,請關注本人,后續會更新更多相關文章!敬請期待!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM