Finding - 夯實(shí)iOS基礎(chǔ)二

objc中向一個(gè)nil對(duì)象發(fā)送消息會(huì)怎么樣?

首先明確一點(diǎn)西壮,在OC中對(duì)一個(gè)nil對(duì)象發(fā)送消息是有效的遗增,不會(huì)有錯(cuò)誤

  1. 對(duì)象是nil,該對(duì)象去調(diào)用一個(gè)方法款青,該方法的返回值是一個(gè)對(duì)象做修,此時(shí)返回的nil(0).如下:

     Dog *littleDog = [[Dog female] born];
    

    方法female的返回值是一個(gè)nil,發(fā)送消息born可都,返回值也是nil(0)

  2. 當(dāng)向一個(gè)nil對(duì)象發(fā)送消息缓待,返回值是一個(gè)結(jié)構(gòu)體,則返回值是0渠牲,結(jié)構(gòu)體中屬性的值都為0

  3. 當(dāng)返回值是一個(gè)指針類型旋炒,則等于sizeof(void *)

  4. 當(dāng)返回值是int,float,NSInteger,double,long double等常量類型,返回值都是0

  5. 當(dāng)返回值不是上述幾種签杈,則返回值為未定義

主要原因是因?yàn)樘闭颍粋€(gè)方法的調(diào)用鼎兽,會(huì)在編譯期間翻譯成objc_msgSend(receiver, selector, args1, args2, ....)。在運(yùn)行時(shí)才真正知道selector的具體實(shí)現(xiàn)铣除。

看下runtime.h中的定義

// 定義在runtime.h
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY; // isa指針指向Meta Class元類谚咬,由于OC中ObjC的類也是一個(gè)對(duì)象,為了處理好之間的關(guān)系尚粘,所以運(yùn)行時(shí)會(huì)創(chuàng)建一個(gè)Meta Class元類择卦,用于存放類的類方法。所以[NSObject alloc]發(fā)送的消息郎嫁,其實(shí)是發(fā)送給NSObject自己秉继,也表明`alloc`是一個(gè)類方法。

#if !__OBJC2__
    Class super_class                       OBJC2_UNAVAILABLE; // 父類
    const char *name                        OBJC2_UNAVAILABLE; // 類名
    long version                            OBJC2_UNAVAILABLE; // 類版本泽铛,默認(rèn)為0
    long info                               OBJC2_UNAVAILABLE; // 類的相關(guān)信息
    long instance_size                      OBJC2_UNAVAILABLE; // 類實(shí)例變量的大小
    struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE; // 成員變量的列表
    struct objc_method_list **methodLists   OBJC2_UNAVAILABLE; // 類中的實(shí)例方法列表
    struct objc_cache *cache                OBJC2_UNAVAILABLE; // 方法的緩存尚辑,主要用于優(yōu)化。即當(dāng)一個(gè)方法被調(diào)用盔腔,會(huì)被放到該緩存中杠茬,方便下次調(diào)用,需要要再去檢索方法列表弛随。
    struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE; // 協(xié)議列表
#endif

} OBJC2_UNAVAILABLE;

簡述對(duì)象調(diào)用方法(向一個(gè)對(duì)象發(fā)送消息)的過程:

對(duì)象的isa指針會(huì)找到該對(duì)象所屬的類瓢喉,之后先從cache中查找方法運(yùn)行,如果有則調(diào)用撵幽,如果沒有則從方法列表及其父類的方法列表查找運(yùn)行灯荧,之后在發(fā)送消息的時(shí)候。objc_msgSend(receiver, selector)不回立即有返回值盐杂,而是調(diào)用具體內(nèi)容時(shí)候才執(zhí)行逗载。

由上可知,本題中對(duì)象一開始就為nil链烈,則對(duì)象的isa指針就是為0地址了厉斟,所以也不會(huì)尋找到其所屬的類,故而無法去尋找方法强衡,也不會(huì)報(bào)錯(cuò)擦秽。

程序什么時(shí)候會(huì)報(bào)unrecognized selector的異常?

這是一個(gè)運(yùn)行時(shí)報(bào)錯(cuò)漩勤,意為無法捕獲到對(duì)應(yīng)的方法感挥,具體的場(chǎng)景是在.h文件中有方法的申明,.m中沒有具體的實(shí)現(xiàn)越败。

亦如上題触幼,對(duì)象的isa指針會(huì)找到對(duì)應(yīng)的類,并從類中的method list和父類的method list中去查找方法的實(shí)現(xiàn)究飞,如果直到最頂層都沒有找到置谦,就會(huì)拋出異常堂鲤。unrecognized selector send to xxx;

當(dāng)出現(xiàn)如上場(chǎng)景是,objc運(yùn)行時(shí)可以讓開發(fā)者對(duì)此種錯(cuò)誤進(jìn)行補(bǔ)救媒峡,使的程序不會(huì)crash掉瘟栖。總的是三種方案:

  1. Method resolution
  2. Fast forwarding
  3. Normal forwarding

具體的如何進(jìn)行補(bǔ)救谅阿,可以參照這個(gè)以前的一篇文章Runtime - 消息轉(zhuǎn)發(fā)和其中對(duì)應(yīng)的demoRuntimeOfMessageTransation

一個(gè)objc對(duì)象如何進(jìn)行內(nèi)存布局半哟?(考慮有父類的情況)

  • 我的理解是一個(gè)實(shí)例對(duì)象(instance class),就是一塊內(nèi)存奔穿。對(duì)于一個(gè)實(shí)例對(duì)象(內(nèi)存)中镜沽,最前面存放的是一個(gè)isa指針,之后存放著父類的實(shí)例變量贱田,在之后存放的是本類的實(shí)例變量。

那如何證明我上面說的呢:

現(xiàn)在我創(chuàng)建一個(gè)如下關(guān)系(箭頭指向代表繼承關(guān)系)的類:SonViewController->SuperViewController->UIViewController

在類SuperViewControllerSonViewController都簡單的設(shè)置了實(shí)例變量嘴脾,然后使用Xcode的debug模式下男摧,使用lldb的p命令打印實(shí)例對(duì)象(instance class)的內(nèi)部結(jié)構(gòu).

(lldb) p *sonVC
(SonViewController) $0 = {
  SuperViewController = {
    UIViewController = {
      UIResponder = {
        NSObject = {
          isa = SonViewController
        }
      }
    }
    // 這是筆者添加的注釋:父類SuperViewController的實(shí)例變量
    _defineSuperString = 0x000c215c @"super string"
    _defineSuperArray = nil
    _innerSuperString = nil
  }
  // 這是筆者添加的注釋:當(dāng)前類SonViewController的實(shí)例變量
  _sonString = 0x000c216c @"son string"
  _innerSonString = nil
}

如上結(jié)構(gòu),證明了——對(duì)于一個(gè)實(shí)例對(duì)象(內(nèi)存)中译打,最前面存放的是一個(gè)isa指針耗拓,之后存放著父類的實(shí)例變量,在之后存放的是本類的實(shí)例變量奏司。

在Objective-C2.0之前其實(shí)我們還可以利用lldb的p命令查看上面結(jié)構(gòu)中isa指向的內(nèi)容結(jié)構(gòu)

這就是一個(gè)Class或者說objc_class結(jié)構(gòu)在內(nèi)存中的樣子乔询。其實(shí)在Objective-C2.0之前這個(gè)結(jié)構(gòu)的定義是暴露給用戶的,但在Objective-C2.0中韵洋,水果公司將它隱藏起來了竿刁。經(jīng)過在網(wǎng)上的查找,發(fā)現(xiàn)在Objective-C2.0之前其定義大致如下:

(gdb) p *dialUNC->isa
$2 = {
    isa = 0x1bebc30, 
    super_class = 0x1bebba4, 
    name = 0xd5dd8d0 "?", 
    version = 45024840, 
    info = 223886032, 
    instance_size = 43102048, 
    ivars = 0x1bebb7c, 
    methodLists = 0xd5dab10, 
    cache = 0x2af0648, 
    protocols = 0xd584050
}

是不是感覺很眼熟的樣子搪缨,沒錯(cuò)食拜,其定義在runtime.h頭文件中,如下

// 定義在runtime.h
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY; // isa指針指向Meta Class元類副编,由于OC中ObjC的類也是一個(gè)對(duì)象负甸,為了處理好之間的關(guān)系,所以運(yùn)行時(shí)會(huì)創(chuàng)建一個(gè)Meta Class元類痹届,用于存放類的類方法呻待。所以[NSObject alloc]發(fā)送的消息,其實(shí)是發(fā)送給NSObject自己队腐,也表明`alloc`是一個(gè)類方法蚕捉。

#if !__OBJC2__
    Class super_class                       OBJC2_UNAVAILABLE; // 父類
    const char *name                        OBJC2_UNAVAILABLE; // 類名
    long version                            OBJC2_UNAVAILABLE; // 類版本,默認(rèn)為0
    long info                               OBJC2_UNAVAILABLE; // 類的相關(guān)信息
    long instance_size                      OBJC2_UNAVAILABLE; // 類實(shí)例變量的大小
    struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE; // 成員變量的列表
    struct objc_method_list **methodLists   OBJC2_UNAVAILABLE; // 類中的實(shí)例方法列表
    struct objc_cache *cache                OBJC2_UNAVAILABLE; // 方法的緩存香到,主要用于優(yōu)化鱼冀。即當(dāng)一個(gè)方法被調(diào)用报破,會(huì)被放到該緩存中,方便下次調(diào)用千绪,需要要再去檢索方法列表充易。
    struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE; // 協(xié)議列表
#endif

} OBJC2_UNAVAILABLE;

看到這里,你是不是暈B了荸型,最上面有一個(gè)指向SonViewController的isa指針盹靴,這里又有一個(gè)定義為Class(typedef struct objc_class *Class;)的isa指針,那它們有什么區(qū)別瑞妇「寰玻快告訴我吧。這里會(huì)有一段論述:

在OC中辕狰,會(huì)存在實(shí)例對(duì)象(instance object)改备,類對(duì)象(class object),和元類對(duì)象(metaclass object)蔓倍。
實(shí)例對(duì)象:很容易理解悬钳。
類對(duì)象:在OC中每一個(gè)類也會(huì)對(duì)應(yīng)一個(gè)類對(duì)象,其中存在放著實(shí)例對(duì)象的成員變量列表偶翅,屬性列表默勾,方法列表,指向父類的superclass指針聚谁。
元類對(duì)象:為了區(qū)分類對(duì)象母剥,運(yùn)行時(shí)庫會(huì)為每個(gè)類創(chuàng)建一個(gè)元類對(duì)象,用于存放該類的類方法形导,其中也有一個(gè)superclass指針环疼,指向元類對(duì)象的父類

所以:實(shí)例對(duì)象中的isa指針指向的是對(duì)象所屬的類對(duì)象,而類對(duì)象中的isa指針指向的是元類對(duì)象骤宣。

下面盜用兩種圖秦爆,用以說明問題:

繼承關(guān)系:

類的繼承關(guān)系

內(nèi)存結(jié)構(gòu):

內(nèi)存關(guān)系結(jié)構(gòu)圖

你還可以從這里看到更詳細(xì)的:
Objective-C內(nèi)存布局

你如何理解OC中的selfsuper

先從網(wǎng)上拷貝一道題:下面的代碼會(huì)輸出什么

@implementation Son : Father
- (id)init
{
    self = [super init];
    if (self) {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}
@end

答案:

本著都自己操作一遍的原則,輸出結(jié)果結(jié)果都是Son

2016-06-26 11:00:35.509 AutoreleasePool[16854:1718107] Son
2016-06-26 11:00:35.509 AutoreleasePool[16854:1718107] Son

題目來自刨根問底Objective-C Runtime(1)- Self & Super

很多人會(huì)想當(dāng)然的認(rèn)為“ super 和 self 類似憔披,應(yīng)該是指向父類的指針吧等限!”。這是很普遍的一個(gè)誤區(qū)芬膝。其實(shí) super 是一個(gè) Magic Keyword望门, 它本質(zhì)是一個(gè)編譯器標(biāo)示符,和 self 是指向的同一個(gè)消息接受者锰霜!他們兩個(gè)的不同點(diǎn)在于:super 會(huì)告訴編譯器筹误,調(diào)用 class 這個(gè)方法時(shí),要去父類的方法癣缅,而不是本類里的厨剪。

當(dāng)使用 self 調(diào)用方法時(shí)哄酝,會(huì)從當(dāng)前類的方法列表中開始找,如果沒有祷膳,就從父類中再找陶衅;而當(dāng)使用 super 時(shí),則從父類的方法列表中開始找直晨。然后調(diào)用父類的這個(gè)方法搀军。

我們可以繼續(xù)探究為什么

如上博文中使用OC中的clang命令重寫Son.m文件(不會(huì)clang命令——使用clang命令行工具編譯鏈接Objective-C應(yīng)用程序)

clang -rewrite-objc Son.m

會(huì)得到一個(gè)Son.cpp文件,非常大勇皇,100000+行罩句,建議用sublime編輯器打開。在文件的尾部敛摘,有對(duì)OC的C++實(shí)現(xiàn)

// @implementation Son


static instancetype _I_Son_init(Son * self, SEL _cmd) {
    if (self = ((Son *(*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("init"))) {
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_w2_nlhh1qs924sg_t6_72yc95b00000gn_T_Son_04092c_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_w2_nlhh1qs924sg_t6_72yc95b00000gn_T_Son_04092c_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"))));
    }
    return self;
}


// @end

雖然本人C++功底不是很強(qiáng)门烂,亦可以從上C++文件中得到如下信息:

[self class]:
被翻譯成objc_msgSend(receiver, selector)=>objc_msgSend(self, sel_registerName("class"))
所以,此處消息接受者即為Son兄淫,故打印Son

[super class]:
被翻譯成objc_ objc_msgSendSuper({receiver, class_getSuperclass(objc_getClass("Son"))}, selector)=>objc_ objc_msgSendSuper({self, class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"))

{self, class_getSuperclass(objc_getClass("Son"))}返回的是Son的父類诅福,但是此處消息的接受者self亦為Son,故而打印[super class]的結(jié)果也是Son

到此拖叙,結(jié)束

擴(kuò)展

上文代碼中的Class *是一個(gè)typedef

typedef struct objc_class *Class;

__rw_objc_super定義:

struct __rw_objc_super { 
    struct objc_object *object; 
    struct objc_object *superClass; 
    __rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {} 
};

如何在OC中使用C++或C

OC是C的一個(gè)超集,在OC中是可以使用C++和C語言的

設(shè)置也非常的簡單赂乐,只需要到XCode工程中的target中薯鳍,到Build Setting中,找到Apple LLVM Compile區(qū)域挨措,選擇Compile Sources As中選擇C++即可

complier_cpp

喜歡請(qǐng)隨意


圖片發(fā)自簡書App
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挖滤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子浅役,更是在濱河造成了極大的恐慌斩松,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件觉既,死亡現(xiàn)場(chǎng)離奇詭異惧盹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)瞪讼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門钧椰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人符欠,你說我怎么就攤上這事嫡霞。” “怎么了希柿?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵诊沪,是天一觀的道長养筒。 經(jīng)常有香客問我,道長端姚,這世上最難降的妖魔是什么晕粪? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮寄锐,結(jié)果婚禮上兵多,老公的妹妹穿的比我還像新娘。我一直安慰自己橄仆,他們只是感情好剩膘,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盆顾,像睡著了一般怠褐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上您宪,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天奈懒,我揣著相機(jī)與錄音,去河邊找鬼宪巨。 笑死磷杏,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捏卓。 我是一名探鬼主播极祸,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼怠晴!你這毒婦竟也來了遥金?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤蒜田,失蹤者是張志新(化名)和其女友劉穎稿械,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冲粤,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡美莫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了色解。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茂嗓。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖科阎,靈堂內(nèi)的尸體忽然破棺而出述吸,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布蝌矛,位于F島的核電站道批,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏入撒。R本人自食惡果不足惜隆豹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望茅逮。 院中可真熱鬧璃赡,春花似錦、人聲如沸献雅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挺身。三九已至侯谁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間章钾,已是汗流浹背墙贱。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贱傀,地道東北人惨撇。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像府寒,于是被迫代替她去往敵國和親串纺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359