對象在收到無法解讀的消息之后會發(fā)生什么情況
消息轉發(fā)分為兩大階段坦刀。第一階段先征詢接收者,所屬的的類沐寺,看其是否能動態(tài)添加方法混坞,以處理當前這個“未知的選擇子”究孕,這叫做“動態(tài)方法解析”厨诸。第二階段涉及“完整的的消息轉發(fā)機制”。這又細分為兩小步禾酱。首先微酬,請接收者看看有沒有其它對象能處理這條消息。若有颤陶,則運行期系統(tǒng)會把消息轉給那個對象得封,于是消息轉發(fā)過程結束,一切如常指郁。若沒有“備援接收者”則啟動完整的消息轉發(fā)機制忙上,運行期系統(tǒng)會把與消息有關的全部細節(jié)都封裝到NSInvocation對象中,再給接收者最后一次機會闲坎,令其設法解決當前還未處理的這條消息疫粥。
動態(tài)方法解析
對象在收到無法解讀的消息后,首先將調用其所屬類的下列類方法
+ (Bool)resolveInstanceMethod:(SEL)selector
該方法的參數(shù)就是那個未知的選擇子腰懂,其返回值為Boolean類型梗逮,表示這個類是否能新增一個實例方法用以處理此選擇子。假如尚未實現(xiàn)的方法不是實例方法而是類方法绣溜,那么運行期系統(tǒng)就會調用另外一個方法,該方法與“resolveInstanceMethod:”類似跋选,叫做“resolveClassMethod:”
使用這種方法的前提是:相關方法的實現(xiàn)代碼已經寫好,只等著運行的時候動態(tài)插在類里面就可以了只搁。此方案常用來實現(xiàn)@dynamic屬性。
備援接收者
當前接收者還有第二次機會能處理未知的選擇子缭付,在這一步中的妖,運行期系統(tǒng)會問它:能不能把這條消息轉發(fā)給其它接收者來處理。與該步驟對應的處理方法如下:
- (id)forwardingTargetForSelector:(SEL)selector
方法參數(shù)代表未知的選擇子,若當前接收者能找到備援對象平夜,則將其返回兼贸,若找不到,就返回nil颜及。我們可以用“組合”來模擬出“多重繼承”的某些特性痊土。在一個對象內部,可能還有一系列其它對象衡载,該對象可經由此方法將能夠處理某選擇子的相關對象返回菩收,這樣的話,在外界看來拴念,好像是該對象親自處理了這些消息似的公般。
完整的消息轉發(fā)
如果轉發(fā)算法已經來到這一步的話刽虹,那么唯一能做的就是啟用完整的消息轉發(fā)機制了。首先創(chuàng)創(chuàng)建NSInvocation對象,把與尚未處理的那條消息有關的全部細節(jié)都封裝于其中。此對象包含選擇子、目標及參數(shù)。在觸發(fā)NSInvocation對象時拢锹,“消息派發(fā)系統(tǒng)”將親自出馬,把消息指派給目標對象。
此步驟會調用下列方法來轉發(fā)消息:
- (void)forwardInvocation:(NSInvocation *)invocation
實現(xiàn)此方法時,若發(fā)現(xiàn)調用操作不應由本類來處理巡莹,則需要調用超類的同名方法。這樣的話额嘿,繼承體系中的每個類都有機會處理此調用請求捕儒,直至NSObject。如果最后調用了NSObject的方法阎毅,那么該方法還會繼而調用“doesNotRecognizeSelector:”以拋出異常扇调,此異常表明選擇子最終未能得到處理熬芜。
完整的例子演示動態(tài)方法解析
以動態(tài)方法解析@dynamic屬性鼓拧,類自動處理相關屬性的存放與獲取操作(編寫一個類似“字典”的對象)
@interface EOCAutoDictionary : NSObject
@property (nonatomic, strong) NSString *string;
@property (nonatomic, strong) NSNumber *number;
@property (nonatomic, strong) NSDate *date;
@property (nonatomic, strong) id opaqueObject;
@end
//需要引入頭文件#import <objc/runtime.h>
@interface EOCAutoDictionary ()
@property (nonatomic, strong) NSMutableDictionary *backingStore;
@end
@implementation EOCAutoDictionary
@dynamic string, number, date, opaqueObject;
- (instancetype)init {
if (self = [super init]) {
_backingStore = [NSMutableDictionary new];
}
return self;
}
id autoDictionaryGetter(id self, SEL _cmd) {
EOCAutoDictionary *typedSelf = (EOCAutoDictionary *)self;
NSMutableDictionary *backingStore = typedSelf.backingStore;
NSString *key = NSStringFromSelector(_cmd);
return [backingStore objectForKey:key];
}
void autoDictionarySetter(id self, SEL _cmd, id value) {
EOCAutoDictionary *typedSelf = (EOCAutoDictionary *)self;
NSMutableDictionary *backingStore = typedSelf.backingStore;
NSString *selectorString = NSStringFromSelector(_cmd);
NSMutableString *key = [selectorString mutableCopy];
[key deleteCharactersInRange:NSMakeRange(key.length - 1, 1)];
[key deleteCharactersInRange:NSMakeRange(0, 3)];
NSString *lowercaseFirstChar = [[key substringToIndex:1] lowercaseString];
[key replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstChar];
if (value) {
[backingStore setObject:value forKey:key];
} else {
[backingStore removeObjectForKey:key];
}
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
NSString *selectorString = NSStringFromSelector(sel);
if ([selectorString hasPrefix:@"set"]) {
class_addMethod(self, sel, (IMP)autoDictionarySetter, "v@:@");
} else {
class_addMethod(self, sel, (IMP)autoDictionaryGetter, "@@:");
}
return YES;
}
@end