什么時候會報unrecognized selector錯誤?
- 對象未實(shí)現(xiàn)該方法。
- 對象已經(jīng)被釋放。
iOS有哪些機(jī)制來避免走到這一步?
-
使用[id respondsToSelector:]進(jìn)行判斷。
forward.jpeg
2.Method resolution
objc運(yùn)行時會調(diào)用+resolveInstanceMethod:或者 +resolveClassMethod:,讓你有機(jī)會提供一個函數(shù)實(shí)現(xiàn)。如果你添加了函數(shù),那運(yùn)行時系統(tǒng)就會重新啟動一次消息發(fā)送的過程,否則 ,運(yùn)行時就會移到下一步,消息轉(zhuǎn)發(fā)(Message Forwarding)。
返回Nil和self,去調(diào)用第三步methodSignatureForSelector和forwarInvocation;返回receiver,如果receiver有響應(yīng)就直接處理,如果沒有就去對應(yīng)的對象內(nèi)去調(diào)用第三步;調(diào)用子類的函數(shù),子類沒有進(jìn)行這幾個方法的重載,在父類處理時返回子類,會死循環(huán)。
3.Fast forwarding
如果目標(biāo)對象實(shí)現(xiàn)了-forwardingTargetForSelector:,Runtime 這時就會調(diào)用這個方法,給你把這個消息轉(zhuǎn)發(fā)給其他對象的機(jī)會。 只要這個方法返回的不是nil和self,整個消息發(fā)送的過程就會被重啟,當(dāng)然發(fā)送的對象會變成你返回的那個對象。否則,就會繼續(xù)Normal Fowarding。 這里叫Fast,只是為了區(qū)別下一步的轉(zhuǎn)發(fā)機(jī)制。因為這一步不會創(chuàng)建任何新的對象,但下一步轉(zhuǎn)發(fā)會創(chuàng)建一個NSInvocation對象,所以相對更快點(diǎn)。
4.Normal forwarding
這一步是Runtime最后一次給你挽救的機(jī)會。首先它會發(fā)送-methodSignatureForSelector:消息獲得函數(shù)的參數(shù)和返回值類型。如果-methodSignatureForSelector:返回nil,Runtime則會發(fā)出-doesNotRecognizeSelector:消息,程序這時也就掛掉了。如果返回了一個函數(shù)簽名,Runtime就會創(chuàng)建一個NSInvocation對象并發(fā)送-forwardInvocation:消息給目標(biāo)對象。
- (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法識別的消息應(yīng)當(dāng)首先被引導(dǎo)到的對象。
Parameters 一個接收者沒有實(shí)現(xiàn)的方法的選擇器。
Return Value 無法識別消息應(yīng)該首先被導(dǎo)向到的對象。
Discussion
如果一個對象實(shí)現(xiàn)(或者繼承)了這個方法,和返回一個非空(non-self)的結(jié)果,那么返回的這個對象被用作新的接收對象和消息分發(fā)到這個新對象。(明顯地,如果你從這個方法返回self,代碼將進(jìn)入一個無限循環(huán)中注:調(diào)試測試發(fā)現(xiàn)不會死循環(huán),而是直接崩潰)
如果你在一個non-root類實(shí)現(xiàn)這個方法,如果你的類對于這個被給的選擇器沒有什么東西返回,那么你應(yīng)該返回調(diào)用super的實(shí)現(xiàn)的結(jié)果。
這方法給一個對象在調(diào)用花費(fèi)更多的forwardInvocation:接管之前,重定向1個未知消息到它的一個機(jī)會。這是有用的當(dāng)你簡單的想重定向消息到另一個對象時和能比常規(guī)的轉(zhuǎn)發(fā)快一個數(shù)量級。當(dāng)轉(zhuǎn)發(fā)的目標(biāo)是在轉(zhuǎn)發(fā)期間捕獲NSInvocation或者篡改參數(shù)或者返回值時,它是沒用的。
-(void)forwardInvocation:(NSInvocation *)anInvocation;
通過子類重載轉(zhuǎn)發(fā)消息到其它對象。
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)一個對象被發(fā)送一個沒有相應(yīng)方法的消息時,運(yùn)行時系統(tǒng)給接收對象一個委托這消息到另一個接收對象的機(jī)會。它通過創(chuàng)建1個NSInvocation對象表示這個消息和發(fā)送給接收對象1個包含這NSInvocation對象作為參數(shù)的forwardInvocation:消息來委托這個消息。這接收對象的forwardInvocation:方法能選擇轉(zhuǎn)發(fā)這個消息到另一個對象。(如果那對象也不能響應(yīng)這個消息,它也將有機(jī)會轉(zhuǎn)發(fā)它)
這forwardInvocation:消息因此允許1個對象和其它的對某一消息將起作用的對象建立關(guān)系。這轉(zhuǎn)發(fā)對象在某種意義上能夠"繼承"轉(zhuǎn)發(fā)消息到那個對象上的一些特性。
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)對象本身不能識別的方法,你必須除了forwardInvocation:外還要重寫methodSignatureForSelector:。轉(zhuǎn)發(fā)消息的原理是用從methodSignatureForSelector:獲取的信息去創(chuàng)建一個NSInvocation對象來進(jìn)行轉(zhuǎn)發(fā)。重載的方法必須為被提供的slector提供一個一致的方法簽名,或者通過預(yù)先制定一個或者通過請求另一個對象。
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個任務(wù):
- 定位一個能響應(yīng)編碼在anInvocation中消息的對象,所有消息的對象不一定相同。
- 用anInvocation發(fā)送消息給對象。anInvocation將保存這個結(jié)果,運(yùn)行時系統(tǒng)將提取和轉(zhuǎn)發(fā)這個結(jié)果給原始的發(fā)送者。
在這個簡單的情況下,1個對象轉(zhuǎn)發(fā)消息只有1個目的地址(像在廈門的例子中假想的friend實(shí)例),1個forwardInvocation:方法能是這樣簡單:
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)多種不同消息,從而避免為每個選擇器寫一個獨(dú)立的方法。forwardInvocation方法也能在響應(yīng)被給的消息時調(diào)用幾個其它的對象,而不是只轉(zhuǎn)發(fā)給1個。
NSObject的forwardInvocation:的實(shí)現(xiàn)簡單調(diào)用doesNotRecognizeSelector:方法,它不轉(zhuǎn)發(fā)任何消息。因此,如果你選擇不實(shí)現(xiàn)forwardInvocation:,發(fā)送1個不能識別的消息到對象將拋出異常。
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
Parameters
一個標(biāo)識返回實(shí)現(xiàn)地址方法的選擇器。當(dāng)接收對象是一個實(shí)例時,選擇器應(yīng)當(dāng)標(biāo)識1個實(shí)例方法;當(dāng)接收對象是一個類時,它應(yīng)當(dāng)是個類方法。
Return Value
返回一個包含由給定的選擇器標(biāo)識的方法描述符的NSMethodSignature對象,如果方法不能找到返回nil。
這個方法被用在協(xié)議的實(shí)現(xiàn)里。這方法也被用在創(chuàng)建1個NSInvocation對象的情況下,比如在轉(zhuǎn)發(fā)消息期間。如果你的對象維護(hù)一個委托或者是能處理1個沒有直接實(shí)現(xiàn)的消息,你應(yīng)該重寫這個方法來返回一個一致的方法簽名。
+(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.