類的本質-類對象

前言

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

1.本質

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

2.如何獲取類對象

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

3.類對象的用法

  • 用來調用類方法
[Dog test];

Class c = [Dog class];
[c test];
  • 用來創(chuàng)建實例對象
Dog *g = [Dog new];

Class c = [Dog class];
Dog *g1 = [c new];

4.類對象的存儲

存儲.png

5.OC實例對象、類對象玉锌、元數(shù)據(jù)名挥、之間關系

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

    • 每一個對象 都是一個類的實例主守。
    • 每一個對象 都有一個名為isa的指針,指向該對象的類躺同。
    • 每一個類都描述了一系列它的實例的特點,包括成員變量的列表,成員函數(shù)的列表等。
    • 每一個對象都可以接受消息,而對象能夠接收的消息列表是保存在它所對應的類中丸逸。
  • 在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這個類發(fā)送名為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)。

    • 元類保存了類方法的列表畏邢。當一個類方法被調用時,元類會首先查找它本身是否有該類方法的實現(xiàn),如果沒有則該元類會向它的父類查找該方法,直到一直找到繼承鏈的頭业扒。
    • 元類(metaclass)也是一個對象,那么元類的isa指針又指向哪里呢?為了設計上的完整,所有的元類的isa指針都會指向一個根元類(root metaclass)。
    • 根元類(root metaclass)本身的isa指針指向自己,這樣就行成了一個閉環(huán)舒萎。上面說??到,一個對象能夠接收的消息列表是保存在它所對應的類中的程储。在實際編程中,我們幾乎不會遇到向元類發(fā)消息的情況,那它的isa 指針在實際上很少用到。不過這么設計保證了面向對象的干凈,即所有事物都是對象,都有isa指針。
    • 由于類方法的定義是保存在元類(metaclass)中,而方法調用的規(guī)則是,如果該類沒有一個方法的實現(xiàn),則向它的父類繼續(xù)查找章鲤。所以為了保證父類的類方法可以在子類中可以被調用,所以子類的元類會繼承父類的元類,換而言之,類對象和元類對象有著同樣的繼承關系摊灭。
  • 下面這張圖或許能夠 讓大家對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)嫌拣,是否遵從某項協(xié)議柔袁,并且能夠看出某對象位于集成體系的哪一部分

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

當對象收到消息時,消息函數(shù)首先根據(jù)該對象的isa
指針找到該對象所對應的類的方法表酝润,并從表中尋找該消息對應的方法selector燎竖。如果找不到,objc_msgSend
將繼續(xù)從父類中尋找要销,直到NSObject
類构回。一旦找到了方法選標, objc_msgSend
則以消息接收者對象為參數(shù)調用疏咐,調用該選標對應的方法實現(xiàn)纤掸。調用方式:objc_msgSend(receiver, selector, arg1, arg2, ...)
這就是在運行時系統(tǒng)中選擇方法實現(xiàn)的方式。在面向對象編程中浑塞,一般稱作方法和消息動態(tài)綁定的過程借跪。
為了加快消息的處理過程,運行時系統(tǒng)通常會將使用過的方法選標和方法實現(xiàn)的地址放入緩存中酌壕。每個類都有一個獨立的緩存掏愁,同時包括繼承的方法和在該類中定義的方法歇由。消息函數(shù)會首先檢查消息接收者對象對應的類的緩存(理論上,如果一個方法被使用過一次托猩,那么它很可能被再次使用)印蓖。如果在緩存中已經(jīng)有了需要的方法選標,則消息僅僅比函數(shù)調用慢一點點京腥。如果程序運行了足夠長的時間赦肃,幾乎每個消息都能在緩存中找到方法實現(xiàn)。程序運行時公浪,緩存也將隨著新的消息的增加而增加他宛。

6.如何查詢類型信息

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

另外璧瞬,需要注意的是男应,我們從集合對象(collection)中獲取的對象搞疗,通常會用到這兩個查詢類型信息的方法。因為從集合對象中取出來的對象不是強類型的(strongly typed),其類型通常是id剥懒∧谥郏回想一下,我們從一個數(shù)組中取出來的對象初橘,其返回值是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)都轉發(fā)給另一個對象(開啟了消息轉發(fā)功能)蓄诽。這樣的對象叫做”代理(proxy)“,此種對象所屬的類均以NSProxy為根類(root class)溜歪。通常情況下若专,如果在此種代理對象上調用class方法,那么返回的是代理對象本身(NSProxy的子類)蝴猪,而非接受代理的對象所屬的類调衰。然而,若是改用“isKindOfClass:”這樣的類型信息查詢方法自阱,那么代理對象就會把這條消息轉給“接受代理的對象(proxy object)”嚎莉。也就是說,這條消息(指isKindOfClass:)的返回值與直接接受代理的對象身上查詢其類型信息所得的結果相同沛豌。因此趋箩,這樣查出來的類對象與直接通過class方法所返回的那個類對象不同,class方法所返回類表示發(fā)起代理的對象加派,而非接受代理的對象叫确。

文/VV木公子(簡書作者)
PS:如非特別說明,所有文章均為原創(chuàng)作品芍锦,著作權歸作者所有竹勉,轉載請聯(lián)系作者獲得授權,并注明出處娄琉,所有打賞均歸本人所有次乓!

如果您是iOS開發(fā)者吓歇,或者對本篇文章感興趣,請關注本人票腰,后續(xù)會更新更多相關文章城看!敬請期待!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末杏慰,一起剝皮案震驚了整個濱河市测柠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逃默,老刑警劉巖鹃愤,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異完域,居然都是意外死亡软吐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門吟税,熙熙樓的掌柜王于貴愁眉苦臉地迎上來凹耙,“玉大人,你說我怎么就攤上這事肠仪⌒けВ” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵异旧,是天一觀的道長意述。 經(jīng)常有香客問我,道長吮蛹,這世上最難降的妖魔是什么荤崇? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮潮针,結果婚禮上术荤,老公的妹妹穿的比我還像新娘。我一直安慰自己每篷,他們只是感情好瓣戚,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著焦读,像睡著了一般子库。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上矗晃,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天仑嗅,我揣著相機與錄音,去河邊找鬼。 笑死无畔,一個胖子當著我的面吹牛,可吹牛的內容都是我干的吠冤。 我是一名探鬼主播浑彰,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拯辙!你這毒婦竟也來了郭变?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤涯保,失蹤者是張志新(化名)和其女友劉穎诉濒,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夕春,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡未荒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了及志。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片片排。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖速侈,靈堂內的尸體忽然破棺而出率寡,到底是詐尸還是另有隱情,我是刑警寧澤倚搬,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布冶共,位于F島的核電站,受9級特大地震影響每界,放射性物質發(fā)生泄漏捅僵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一盆犁、第九天 我趴在偏房一處隱蔽的房頂上張望命咐。 院中可真熱鬧,春花似錦谐岁、人聲如沸醋奠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窜司。三九已至,卻和暖如春航揉,著一層夾襖步出監(jiān)牢的瞬間塞祈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工帅涂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留议薪,地道東北人尤蛮。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像斯议,于是被迫代替她去往敵國和親产捞。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內容