目次
OCのインスタンスオブジェクトは、というメソッドを呼び出します发送消息
オブジェクトにメッセージを送信するときに、オブジェクトのメソッドリストも、対応する継承チェーンのメソッドリストも、メッセージのメソッド実装を見つけることができない場合、メッセージ転送メカニズムがトリガーされます。
たとえば、次のコード:
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
- (void)sendMessage:(NSString*)message;
@end
NS_ASSUME_NONNULL_END
Person *p = [[Person alloc]init];
[p sendMessage:@"hello"];
.hファイルで宣言されたメソッドがあり、このメソッドは.mに実装されておらず、Personはこの実装されていないメソッドを呼び出して、メッセージ転送メカニズムをトリガーします。
PersonオブジェクトはsendMessage:メソッドを呼び出します。OCの基本的な実装は実際には次のとおりです。
objc_msgSend([Person new],@selector(sendMessage:),@"hello");
それを実装する方法はありません。私たちはそれを呼び出します。メッセージ転送メカニズムは、認識されない単語に遭遇したときに辞書を検索するようなものです。sendMessage:メソッドを見つける場所、objc_msgSendは[Person new]ディクショナリでそれを見つけます。部首はメソッド番号@selectorで、単語@ "hello"を見つけます。
isaポインタのルックアッププロセスを見てみましょう。
現在のオブジェクトのisaポインタからクラスを見つけ、メソッドリストでメソッドを見つけて直接実装します。現在のリストにない場合は、継承ツリーでクラスを探し、メソッド番号とIMPの間のマッピング関係からメソッドの実装を見つけます。なし、今回はメッセージ転送メカニズムに入ります。
メッセージ転送のフローチャート:
1.動的メソッド分析
最初のステップ:オブジェクトが解釈できないメッセージを受信した後、最初に+(BOOL)resolveInstanceMethod:(SEL)sel
または+(BOOL)resolveClassMethod:(SEL)selを呼び出し、処理および処理のために動的に追加されたメソッドがあるかどうかを尋ねます。例は次のとおりです
+(BOOL)resolveInstanceMethod:(SEL)sel {
// 方法匹配
NSString *methodName = NSStringFromSelector(sel);
if ([methodName isEqualToString:@"sendMessage:"]) {
return class_addMethod(self, sel, (IMP)sendMessage, "v@:@");
}
return [super resolveInstanceMethod:sel];//如果没有就走继承树方法
}
動的メソッド分析は、処理メソッドを追加することです
void sendMessage(id self,SEL _cmd,NSString* message) {
NSLog(@"---%@",message);
}
v @:@これは、vが戻り値がvoid型であることを示します。上記のメソッドのパラメーターid selfは@で表され、id型は@で表され、SELはコロン:で表されるメソッド番号であり、自分自身によって渡されるパラメーターNSString型は@で表されます。(id self、SEL _cmdがデフォルトのパラメーターです)
実行後、メッセージhelloが出力されていることがわかります。
2.早送り
呼び出されたメソッドの実装が実行時のメッセージ転送の最初のステップで見つからない場合、現在の受信者はそれを処理する2回目のチャンスがあります。このとき、ランタイムシステムは-(id)forwardingTargetForSelector:(SEL)aSelectorメソッドを呼び出します。これにより、処理可能なオブジェクトが返され、ランタイムシステムは返されたオブジェクトに基づいて検索し、見つかった場合は、対応するメソッドの実装にジャンプします。 、メッセージ転送が終了します。
早送りは簡単です、受信者を見つけてください
-(id)forwardingTargetForSelector:(SEL)aSelector {
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [SpareWheel new];//备用接受者
}
return [super forwardingTargetForSelector:aSelector];//走继承树方法
}
sendMessage:メソッドは、探しているレシーバーの.mに実装されています
#import "SpareWheel.h"
@implementation SpareWheel
- (void)sendMessage:(NSString*)message {
NSLog(@"---SpareWheel say %@",message);
}
@end
結果は次のとおりです。
3.遅い転送
- メソッド署名
- メッセージ転送
#pragma mark - 方法签名
-(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
NSString *methodName = NSStringFromSelector(aSelector);
if ([methodName isEqualToString:@"sendMessage:"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
}
return [super methodSignatureForSelector:aSelector];
}
上記のメソッド署名情報はNSInvocationに格納され、メソッド番号はSEL sel = [anInvocationselector]を介して取得できます。
//这个方法是找一个 处理者
-(void)forwardInvocation:(NSInvocation *)anInvocation {
//消息转发
SEL sel = [anInvocation selector];
SpareWheel *tempObjc = [SpareWheel new];// 找一个处理者
if ([tempObjc respondsToSelector:sel]) {
// 如果这个处理者响应了这个方法,就指定它作为目标对象,处理当前的方法
[anInvocation invokeWithTarget:tempObjc];
}else{
[super forwardInvocation:anInvocation];
}
}
結果は次のとおりです。
上記のメソッドのいずれも実装されていない場合、-(void)doesNotRecognizeSelector:(SEL)aSelectorメソッドが実行されます。プログラムのクラッシュを防ぐために、この方法でプログラムの堅牢性を高めることができます。
//找不到方法就执行这个
-(void)doesNotRecognizeSelector:(SEL)aSelector {
NSLog(@"找不到方法");
}
demo地址