objc中向一個(gè)nil對(duì)象發(fā)送消息會(huì)怎么樣?
首先明確一點(diǎn)西壮,在OC中對(duì)一個(gè)nil對(duì)象發(fā)送消息是有效的遗增,不會(huì)有錯(cuò)誤
-
對(duì)象是
nil
,該對(duì)象去調(diào)用一個(gè)方法款青,該方法的返回值是一個(gè)對(duì)象做修,此時(shí)返回的nil(0).如下:Dog *littleDog = [[Dog female] born];
方法female的返回值是一個(gè)
nil
,發(fā)送消息born可都,返回值也是nil
(0) 當(dāng)向一個(gè)
nil
對(duì)象發(fā)送消息缓待,返回值是一個(gè)結(jié)構(gòu)體,則返回值是0渠牲,結(jié)構(gòu)體中屬性的值都為0當(dāng)返回值是一個(gè)指針類型旋炒,則等于
sizeof(void *)
當(dāng)返回值是
int
,float
,NSInteger
,double
,long double
等常量類型,返回值都是0當(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掉瘟栖。總的是三種方案:
- Method resolution
- Fast forwarding
- 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
在類SuperViewController
和SonViewController
都簡單的設(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)系:
內(nèi)存結(jié)構(gòu):
你還可以從這里看到更詳細(xì)的:
Objective-C內(nèi)存布局
你如何理解OC中的self
和super
先從網(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++即可
喜歡請(qǐng)隨意