什么時(shí)候會(huì)報(bào)unrecognized selector錯(cuò)誤胞皱?
- 對(duì)象未實(shí)現(xiàn)該方法邪意。
- 對(duì)象已經(jīng)被釋放反砌。
iOS有哪些機(jī)制來避免走到這一步宴树?
-
使用[id respondsToSelector:]進(jìn)行判斷。
forward.jpeg
2.Method resolution
objc運(yùn)行時(shí)會(huì)調(diào)用+resolveInstanceMethod:或者 +resolveClassMethod:酒贬,讓你有機(jī)會(huì)提供一個(gè)函數(shù)實(shí)現(xiàn)翠霍。如果你添加了函數(shù),那運(yùn)行時(shí)系統(tǒng)就會(huì)重新啟動(dòng)一次消息發(fā)送的過程寒匙,否則 ,運(yùn)行時(shí)就會(huì)移到下一步考蕾,消息轉(zhuǎn)發(fā)(Message Forwarding)。
返回Nil和self肖卧,去調(diào)用第三步methodSignatureForSelector和forwarInvocation;返回receiver拦赠,如果receiver有響應(yīng)就直接處理,如果沒有就去對(duì)應(yīng)的對(duì)象內(nèi)去調(diào)用第三步矛紫;調(diào)用子類的函數(shù),子類沒有進(jìn)行這幾個(gè)方法的重載颊咬,在父類處理時(shí)返回子類,會(huì)死循環(huán)喳篇。
3.Fast forwarding
如果目標(biāo)對(duì)象實(shí)現(xiàn)了-forwardingTargetForSelector:态辛,Runtime 這時(shí)就會(huì)調(diào)用這個(gè)方法,給你把這個(gè)消息轉(zhuǎn)發(fā)給其他對(duì)象的機(jī)會(huì)奏黑。 只要這個(gè)方法返回的不是nil和self,整個(gè)消息發(fā)送的過程就會(huì)被重啟熟史,當(dāng)然發(fā)送的對(duì)象會(huì)變成你返回的那個(gè)對(duì)象。否則碘菜,就會(huì)繼續(xù)Normal Fowarding。 這里叫Fast忍啸,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機(jī)制。因?yàn)檫@一步不會(huì)創(chuàng)建任何新的對(duì)象计雌,但下一步轉(zhuǎn)發(fā)會(huì)創(chuàng)建一個(gè)NSInvocation對(duì)象,所以相對(duì)更快點(diǎn)玫霎。
4.Normal forwarding
這一步是Runtime最后一次給你挽救的機(jī)會(huì)传泊。首先它會(huì)發(fā)送-methodSignatureForSelector:消息獲得函數(shù)的參數(shù)和返回值類型眷细。如果-methodSignatureForSelector:返回nil,Runtime則會(huì)發(fā)出-doesNotRecognizeSelector:消息鹃祖,程序這時(shí)也就掛掉了。如果返回了一個(gè)函數(shù)簽名校读,Runtime就會(huì)創(chuàng)建一個(gè)NSInvocation對(duì)象并發(fā)送-forwardInvocation:消息給目標(biāo)對(duì)象祖能。
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
- (void)forwardInvocation:(NSInvocation *)anInvocation OBJC_SWIFT_UNAVAILABLE("");
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector OBJC_SWIFT_UNAVAILABLE("");
文檔翻譯:
-(id)forwardingTargetForSelector:(SEL)aSelector
返回?zé)o法識(shí)別的消息應(yīng)當(dāng)首先被引導(dǎo)到的對(duì)象。
Parameters 一個(gè)接收者沒有實(shí)現(xiàn)的方法的選擇器雁芙。
Return Value 無法識(shí)別消息應(yīng)該首先被導(dǎo)向到的對(duì)象。
Discussion
如果一個(gè)對(duì)象實(shí)現(xiàn)(或者繼承)了這個(gè)方法兔甘,和返回一個(gè)非空(non-self)的結(jié)果,那么返回的這個(gè)對(duì)象被用作新的接收對(duì)象和消息分發(fā)到這個(gè)新對(duì)象洞焙。(明顯地拯啦,如果你從這個(gè)方法返回self,代碼將進(jìn)入一個(gè)無限循環(huán)中注:調(diào)試測(cè)試發(fā)現(xiàn)不會(huì)死循環(huán)唁情,而是直接崩潰)
如果你在一個(gè)non-root類實(shí)現(xiàn)這個(gè)方法,如果你的類對(duì)于這個(gè)被給的選擇器沒有什么東西返回荠瘪,那么你應(yīng)該返回調(diào)用super的實(shí)現(xiàn)的結(jié)果夯巷。
這方法給一個(gè)對(duì)象在調(diào)用花費(fèi)更多的forwardInvocation:接管之前,重定向1個(gè)未知消息到它的一個(gè)機(jī)會(huì)喷兼。這是有用的當(dāng)你簡(jiǎn)單的想重定向消息到另一個(gè)對(duì)象時(shí)和能比常規(guī)的轉(zhuǎn)發(fā)快一個(gè)數(shù)量級(jí)后雷。當(dāng)轉(zhuǎn)發(fā)的目標(biāo)是在轉(zhuǎn)發(fā)期間捕獲NSInvocation或者篡改參數(shù)或者返回值時(shí)吠各,它是沒用的勉抓。
-(void)forwardInvocation:(NSInvocation *)anInvocation;
通過子類重載轉(zhuǎn)發(fā)消息到其它對(duì)象。
Discussion
When an object is sent a message for which it has no corresponding method, the runtime system gives the receiver an opportunity to delegate the message to another receiver. It delegates the message by creating an NSInvocation object representing the message and sending the receiver a forwardInvocation: message containing this NSInvocation object as the argument. The receiver’s forwardInvocation: method can then choose to forward the message to another object. (If that object can’t respond to the message either, it too will be given a chance to forward it.)
The forwardInvocation: message thus allows an object to establish relationships with other objects that will, for certain messages, act on its behalf. The forwarding object is, in a sense, able to “inherit” some of the characteristics of the object it forwards the message to.
當(dāng)一個(gè)對(duì)象被發(fā)送一個(gè)沒有相應(yīng)方法的消息時(shí)纵散,運(yùn)行時(shí)系統(tǒng)給接收對(duì)象一個(gè)委托這消息到另一個(gè)接收對(duì)象的機(jī)會(huì)隐圾。它通過創(chuàng)建1個(gè)NSInvocation對(duì)象表示這個(gè)消息和發(fā)送給接收對(duì)象1個(gè)包含這NSInvocation對(duì)象作為參數(shù)的forwardInvocation:消息來委托這個(gè)消息。這接收對(duì)象的forwardInvocation:方法能選擇轉(zhuǎn)發(fā)這個(gè)消息到另一個(gè)對(duì)象蜜笤。(如果那對(duì)象也不能響應(yīng)這個(gè)消息,它也將有機(jī)會(huì)轉(zhuǎn)發(fā)它)
這forwardInvocation:消息因此允許1個(gè)對(duì)象和其它的對(duì)某一消息將起作用的對(duì)象建立關(guān)系盐碱。這轉(zhuǎn)發(fā)對(duì)象在某種意義上能夠"繼承"轉(zhuǎn)發(fā)消息到那個(gè)對(duì)象上的一些特性。
Important
To respond to methods that your object does not itself recognize, you must override methodSignatureForSelector: in addition to forwardInvocation:. The mechanism for forwarding messages uses information obtained from methodSignatureForSelector: to create the NSInvocation
object to be forwarded. Your overriding method must provide an appropriate method signature for the given selector, either by pre formulating one or by asking another object for one.
為了響應(yīng)對(duì)象本身不能識(shí)別的方法瓮顽,你必須除了forwardInvocation:外還要重寫methodSignatureForSelector:。轉(zhuǎn)發(fā)消息的原理是用從methodSignatureForSelector:獲取的信息去創(chuàng)建一個(gè)NSInvocation對(duì)象來進(jìn)行轉(zhuǎn)發(fā)聘惦。重載的方法必須為被提供的slector提供一個(gè)一致的方法簽名,或者通過預(yù)先制定一個(gè)或者通過請(qǐng)求另一個(gè)對(duì)象儒恋。
An implementation of the forwardInvocation: method has two tasks:
· To locate an object that can respond to the message encoded in anInvocation. This object need not be the same for all messages.
· To send the message to that object using anInvocation. ** anInvocation** will hold the result, and the runtime system will extract and deliver this result to the original sender.
In the simple case, in which an object forwards messages to just one destination (such as the hypothetical friend instance variable in the example below), a forwardInvocation: method could be as simple as this:
forwardInvocation:方法的實(shí)現(xiàn)有2個(gè)任務(wù):
- 定位一個(gè)能響應(yīng)編碼在anInvocation中消息的對(duì)象诫尽,所有消息的對(duì)象不一定相同。
- 用anInvocation發(fā)送消息給對(duì)象牧嫉。anInvocation將保存這個(gè)結(jié)果,運(yùn)行時(shí)系統(tǒng)將提取和轉(zhuǎn)發(fā)這個(gè)結(jié)果給原始的發(fā)送者酣藻。
在這個(gè)簡(jiǎn)單的情況下,1個(gè)對(duì)象轉(zhuǎn)發(fā)消息只有1個(gè)目的地址(像在廈門的例子中假想的friend實(shí)例)辽剧,1個(gè)forwardInvocation:方法能是這樣簡(jiǎn)單:
Listing 1
- (void)forwardInvocation:(NSInvocation *)invocation{
SEL aSelector = [invocation selector];
if ([friend respondsToSelector:aSelector])
[invocation invokeWithTarget:friend];
else
[super forwardInvocation:invocation];
}
The message that’s forwarded must have a fixed number of arguments; variable numbers of arguments (in the style of printf()
) are not supported.
The return value of the forwarded message is returned to the original sender. All types of return values can be delivered to the sender: id
types, structures, double-precision floating-point numbers.
Implementations of the forwardInvocation:
method can do more than just forward messages. forwardInvocation:
can, for example, be used to consolidate code that responds to a variety of different messages, thus avoiding the necessity of having to write a separate method for each selector. A forwardInvocation:
method might also involve several other objects in the response to a given message, rather than forward it to just one.
NSObject’s implementation of forwardInvocation:
simply invokes the doesNotRecognizeSelector:
method; it doesn’t forward any messages. Thus, if you choose not to implement forwardInvocation:
, sending unrecognized messages to objects will raise exceptions.
消息轉(zhuǎn)發(fā)必須有固定數(shù)量的參數(shù),不支持可變數(shù)量的參數(shù)(像printf()這種格式的)怕轿。
轉(zhuǎn)發(fā)消息的返回值被原始的發(fā)送者返回偷崩。所有返回值的類型都能被發(fā)送給發(fā)送者:id類型辟拷,結(jié)構(gòu)體,雙精度浮點(diǎn)數(shù)阐斜。
forwardInvocation:方法的實(shí)現(xiàn)能比只轉(zhuǎn)發(fā)消息做更多的事情衫冻。例如,forwardInvocation:能被用來合并代碼來響應(yīng)多種不同消息谒出,從而避免為每個(gè)選擇器寫一個(gè)獨(dú)立的方法羽杰。forwardInvocation方法也能在響應(yīng)被給的消息時(shí)調(diào)用幾個(gè)其它的對(duì)象,而不是只轉(zhuǎn)發(fā)給1個(gè)到推。
NSObject的forwardInvocation:的實(shí)現(xiàn)簡(jiǎn)單調(diào)用doesNotRecognizeSelector:方法考赛,它不轉(zhuǎn)發(fā)任何消息。因此莉测,如果你選擇不實(shí)現(xiàn)forwardInvocation:颜骤,發(fā)送1個(gè)不能識(shí)別的消息到對(duì)象將拋出異常。
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
Parameters
一個(gè)標(biāo)識(shí)返回實(shí)現(xiàn)地址方法的選擇器捣卤。當(dāng)接收對(duì)象是一個(gè)實(shí)例時(shí)忍抽,選擇器應(yīng)當(dāng)標(biāo)識(shí)1個(gè)實(shí)例方法;當(dāng)接收對(duì)象是一個(gè)類時(shí)董朝,它應(yīng)當(dāng)是個(gè)類方法鸠项。
Return Value
返回一個(gè)包含由給定的選擇器標(biāo)識(shí)的方法描述符的NSMethodSignature對(duì)象,如果方法不能找到返回nil子姜。
這個(gè)方法被用在協(xié)議的實(shí)現(xiàn)里祟绊。這方法也被用在創(chuàng)建1個(gè)NSInvocation對(duì)象的情況下牧抽,比如在轉(zhuǎn)發(fā)消息期間讲坎。如果你的對(duì)象維護(hù)一個(gè)委托或者是能處理1個(gè)沒有直接實(shí)現(xiàn)的消息晨炕,你應(yīng)該重寫這個(gè)方法來返回一個(gè)一致的方法簽名府瞄。
+(BOOL)resolveInstanceMethod:(SEL)sel;
Parameters
name
The name of a selector to resolve.
Return Value
Dynamically provides an implementation for a given selector for an instance method.
YES if the method was found and added to the receiver, otherwise NO.
Discussion
This method and resolveClassMethod:
allow you to dynamically provide an implementation for a given selector.
An Objective-C method is simply a C function that take at least two arguments—self and _cmd. Using the class_addMethod
function, you can add a function to a class as a method. Given the following function:
Listing 1
void dynamicMethodIMP(id self, SEL _cmd){
// implementation ....
}
you can use resolveInstanceMethod: to dynamically add it to a class as a method (called resolveThisMethodDynamically) like this:
Listing 2
+ (BOOL) resolveInstanceMethod:(SEL)aSEL{
if (aSEL == @selector(resolveThisMethodDynamically)) {
class_addMethod([self class], aSEL, (IMP) dynamicMethodIMP, "v@:");
return YES;
}
return [super resolveInstanceMethod:aSel];
}
Special Considerations
This method is called before the Objective-C forwarding mechanism is invoked. If respondsToSelector:
or instancesRespondToSelector:
is invoked, the dynamic method resolver is given the opportunity to provide an IMP
for the given selector first.