关于消息转发机制可能用到的也不是很多,但是在这里也记录下,它的几个用法。如果我们想让一个类没有实现的方法想让另一个类去做,其实就要用到消息转发的机制了。
就好比我们有一个Person类,如果这个类声明了eat方法但是没有去实现这个eat方法,然后我们在创建一个Person类对象的时候,然后去调用eat方法,程序毫无疑问会崩溃。
关于消息转发首先有几个方法我们需要去知道的就是下面的这几个方法
//这个是消息的重定向如果返回消息转发的接受者如果为nil,就继续去调用了forwardInvocation方法
- (id)forwardingTargetForSelector:(SEL)aSelector
//拿到方法签名配发消息
- (void)forwardInvocation:(NSInvocation *)anInvocation
//生成方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
//如果类方法不能响应,就会来到这个方法
+ (BOOL)resolveClassMethod:(SEL)sel
//如果对象方法不能响应就来到这个方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
//就是如果没有实现方法的话就会抛出异常
- (void)doesNotRecognizeSelector:(SEL)aSelector
有张图我们可以看一下
这里需要注意的是NSInvocation是用来包装方法和对应的对象,它可以存储方法的名称,对应的对象,对应的参数。介绍下他的用法
//签名对象的作用:用于获取参数的个数和方法的返回值,这个参数一定要是正确的,返回值可以和我们传入的@selector不一样
NSMethodSignature*signature = [Student instanceMethodSignatureForSelector:@selector(eat:)];
//1、创建NSInvocation对象
NSInvocation*invocation = [NSInvocation invocationWithMethodSignature:signature];
//需要注意这里需要一个强引用
invocation.target = self.student;
NSString * argument=@"kkkllll";
//设置参数的索引时不能从0开始,因为0已经被self占用,1已经被_cmd占用
[invocation setArgument:&argument atIndex:2];
invocation.selector = @selector(eat:);
[invocation invoke];
如果是类方法也类似
//但这里需要注意的是,如果我们这里下面直接使用去使用Student类去调用某个方法,这样会直接崩溃就比如如果写这个就会崩溃
//NSMethodSignature*signature = [Student instanceMethodSignatureForSelector:@selector(run:)];
//所以用这个代替
NSMethodSignature*signature = [ViewController instanceMethodSignatureForSelector:@selector(hello:)];
//1、创建NSInvocation对象
NSInvocation*invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = [Student class];
NSString * argument=@"kkkllll";
[invocation setArgument:&argument atIndex:2];
invocation.selector = @selector(run:);
[invocation invoke];
如果上面的
+ (BOOL)resolveInstanceMethod:(SEL)sel没有去做相应的处理的话,那么就会来到下面的这个方法,我们在这里面可以做到一个消息转发,转发给另一个类的对象去实现
- (id)forwardingTargetForSelector:(SEL)aSelector {
return [[B alloc] init];
}
如果target返回的是nil的话就会走下面的这个方法
// 生成方法签名.
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
// 1. 转为字符串
NSString *sel = NSStringFromSelector(aSelector);
// 2.进行判断 手动生成签名
if ([sel isEqualToString:@"run"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
} else {
return [super methodSignatureForSelector:aSelector];
}
}
// 拿到这个方法签名之后去配发消息
- (void)forwardInvocation:(NSInvocation *)anInvocation {
// 1.拿到这个消息
SEL selector = [anInvocation selector];
// 2.转发消息.
Dog *dog = [[Dog alloc] init];
if ([dog respondsToSelector:selector]) {
// 调用这个对象,进行转发
[anInvocation invokeWithTarget:dog];
} else {
//到了这里如果没有实现下面的方法程序是不会崩溃的
[super forwardInvocation:anInvocation];
}
}