在簡(jiǎn)書看到大牛的知識(shí)點(diǎn),發(fā)現(xiàn)很多知識(shí)點(diǎn)自己一知半解,能做項(xiàng)目但理論不夠扎實(shí),默默地去百度總結(jié)一下.放到這里和大家交流交流.
如有侵權(quán),告知即刪!
01.objc在向一個(gè)對(duì)象發(fā)送消息時(shí),發(fā)生了什么?
objective-c 的 Runtime 鑄就了它動(dòng)態(tài)語言的特性,這些深層次的知識(shí)雖然平時(shí)寫代碼用的少一些,但是卻是每個(gè) Objc 程序員需要了解的。
Objc Runtime使得C具有了面向?qū)ο竽芰?,在程序運(yùn)行時(shí)創(chuàng)建,檢查,修改類、對(duì)象和它們的方法。可以使用runtime的一系列方法實(shí)現(xiàn)。
附上OC中一個(gè)類的底層數(shù)據(jù)結(jié)構(gòu)
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY; //isa指針指向Meta Class,因?yàn)镺bjc的類的本身也是一個(gè)Object,為了處理這個(gè)關(guān)系,r untime就創(chuàng)造了Meta Class,當(dāng)給類發(fā)送[NSObject alloc]這樣消息時(shí),實(shí)際上是把這個(gè)消息發(fā)給了Class Object
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE; // 父類
const char *name OBJC2_UNAVAILABLE; // 類名
long version OBJC2_UNAVAILABLE; // 類的版本信息,默認(rèn)為0
long info OBJC2_UNAVAILABLE; // 類信息,供運(yùn)行期使用的一些位標(biāo)識(shí)
long instance_size OBJC2_UNAVAILABLE; // 該類的實(shí)例變量大小
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 該類的成員變量鏈表
struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定義的鏈表
struct objc_cache *cache OBJC2_UNAVAILABLE; // 方法緩存,對(duì)象接到一個(gè)消息會(huì)根據(jù)isa指針查找消息對(duì)象,這時(shí)會(huì)在method Lists中遍歷,如果cache了,常用的方法調(diào)用時(shí)就能夠提高調(diào)用的效率。
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
OC中一個(gè)類的對(duì)象實(shí)例的[數(shù)據(jù)結(jié)構(gòu)],就是定義了一個(gè)
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
向object發(fā)送消息時(shí),Runtime庫(kù)會(huì)根據(jù)object的isa指針找到這個(gè)實(shí)例object所屬于的類,然后在類的方法列表以及父類方法列表尋找對(duì)應(yīng)的方法運(yùn)行。id是一個(gè)objc_object結(jié)構(gòu)類型的指針,這個(gè)類型的對(duì)象能夠轉(zhuǎn)換成任何一種對(duì)象。
然后再來看看消息發(fā)送的函數(shù):objc_msgSend函數(shù)
在引言中已經(jīng)對(duì)objc_msgSend進(jìn)行了一點(diǎn)介紹,看起來像是objc_msgSend返回了數(shù)據(jù),其實(shí)objc_msgSend從不返回?cái)?shù)據(jù)而是你的方法被調(diào)用后返回了數(shù)據(jù)。下面詳細(xì)敘述下消息發(fā)送步驟:
1.檢測(cè)這個(gè) selector 是不是要忽略的。比如 Mac OS X 開發(fā),有了垃圾回收就不理會(huì) retain,release 這些函數(shù)了。
2.檢測(cè)這個(gè) target 是不是 nil 對(duì)象。ObjC 的特性是允許對(duì)一個(gè) nil 對(duì)象執(zhí)行任何一個(gè)方法不會(huì) Crash,因?yàn)闀?huì)被忽略掉。
3.如果上面兩個(gè)都過了,那就開始查找這個(gè)類的 IMP,先從 cache 里面找,完了 找得到就跳到對(duì)應(yīng)的函數(shù)去執(zhí)行。
4.如果 cache 找不到就找一下方法分發(fā)表。
5.如果分發(fā)表找不到就到超類的分發(fā)表去找,一直找,直到找到NSObject類為止。
6.如果還找不到就要開始進(jìn)入動(dòng)態(tài)方法解析了,后面會(huì)提到。
02.什么時(shí)候會(huì)報(bào)unrecognized selector錯(cuò)誤?iOS有哪些機(jī)制來避免走到這一步?
當(dāng)調(diào)用對(duì)象的某個(gè)方法的時(shí)候, 如果在當(dāng)前類中沒有找到此方法, 那么就到當(dāng)前類的父類中去尋找, 如果在父類中沒有找到, 那么就去父類的父類中去尋找, 一直找到 NSObject 都沒有這個(gè)方法, 就會(huì)報(bào) Unrecognized selector 的異常.
但是在這之前, objc 的運(yùn)行時(shí)會(huì)給出三次拯救程序崩潰的機(jī)會(huì).
第一次: 動(dòng)態(tài)添加一個(gè)新方法并執(zhí)行的機(jī)會(huì)
[objc]
+ (bool)resolveInstanceMethod:(sel)sel{
}
當(dāng)系統(tǒng)第一次找不到某個(gè)方法的時(shí)候, 會(huì)自動(dòng)調(diào)用這個(gè)方法, 用來給程序添加一個(gè)新方法并執(zhí)行的機(jī)會(huì).
第二次: 當(dāng)系統(tǒng)調(diào)用上一個(gè)方法后未能實(shí)現(xiàn)添加新的方法, 則系統(tǒng)會(huì)再來調(diào)用下面的這個(gè)方法, 這個(gè)方法是系統(tǒng)提供的一個(gè)將 SEL 轉(zhuǎn)給其他對(duì)象的機(jī)會(huì)
[objc]
- (id)forwardingTargetForselector:(sel)aselctor{
}
第三次: 當(dāng) forwardingTargetForselector 返回的 nil 或者 self 時(shí), 會(huì)進(jìn)入到這個(gè)方法, 這個(gè)方法是拯救程序的最后一步.
這個(gè)方法用來返回一個(gè)方法簽名, 在由后面的 forwardInvocation: 去執(zhí)行
[objc]
- (NSMethodSigature *)methodSignatrueForseletor:(sel)aselector{
}
如果上面的方法不返回 nil, 則會(huì)來到這個(gè)方法里具體執(zhí)行
[objc]
-(void)forwardInvocation:(NSInvocation *)anInvocation{
在這里會(huì)調(diào)用自己對(duì)象的其他方法, 也可以調(diào)用其他函數(shù)
甚至還可以調(diào)用多個(gè)不同對(duì)象的多個(gè)方法
}
03.runtime如何實(shí)現(xiàn)weak變量的自動(dòng)置nil?
要實(shí)現(xiàn) weak 屬性,首先要搞清楚 weak 屬性的特點(diǎn):
weak 此特質(zhì)表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)。為這種屬性設(shè)置新值時(shí),設(shè)置方法既不保留新值,也不釋放舊值。此特質(zhì)同 assign 類似, 然而在屬性所指的對(duì)象遭到摧毀時(shí),屬性值也會(huì)清空(nil out)。
那么 runtime 如何實(shí)現(xiàn) weak 變量的自動(dòng)置nil?
runtime 對(duì)注冊(cè)的類, 會(huì)進(jìn)行布局,對(duì)于 weak 對(duì)象會(huì)放入一個(gè) hash 表中。 用 weak 指向的對(duì)象內(nèi)存地址作為 key,當(dāng)此對(duì)象的引用計(jì)數(shù)為0的時(shí)候會(huì) dealloc,假如 weak 指向的對(duì)象內(nèi)存地址是a,那么就會(huì)以a為鍵, 在這個(gè) weak 表中搜索,找到所有以a為鍵的 weak 對(duì)象,從而設(shè)置為 nil。