第三阶段:消息转发
- 图解消息转发流程
- 代码展示
// Teacher.h文件
@interface Teacher : NSObject
- (void)testForwarding;
@end
// Teacher.m 文件
#import "Teacher.h"
#import <objc/runtime.h>
#import "Dog.h"
@implementation Teacher
// 第一步,来到这里
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(testForwarding)) {
return [[Dog alloc] init]; // 返回一个对象
}
return [super forwardingTargetForSelector:aSelector];
}
// 第二步,如果第一步返回了nil,就尝试来到这里
// 调用方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(testForwarding)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
// 返回了方法签名后,会来到调用 - (void)forwardInvocation:(NSInvocation *)anInvocation 方法
}
return [super methodSignatureForSelector:aSelector];
}
// NSInvocation 封装了一个方法调用,包括:方法调用者,方法名,方法参数
// anInvocation.target
// anInvocation.selector
// [anInvocation getArgument:NULL atIndex:0]
- (void)forwardInvocation:(NSInvocation *)anInvocation {
anInvocation.target = [[Dog alloc] init]; // 设置调用者
[anInvocation invoke]; // 发起调用
}
@end
// Dog.h 文件
@interface Dog : NSObject
- (void)testForwarding;
@end
// Dog.m文件
#import "Dog.h"
@implementation Dog
- (void)testForwarding {
NSLog(@"Dog-testForwarding");
}
@end
- 方法调用
Teacher *teacher = [[Teacher alloc] init];
[teacher testForwarding];
打印结果:Dog-testForwarding
- 当第一步没有实现,或者返回nil
- 则可以在方法里面做你任何想做的事情
// 第一步,来到这里
- (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(testForwarding)) {
// return [[Dog alloc] init]; // 返回一个对象
return nil;
}
return [super forwardingTargetForSelector:aSelector];
}
// 第二步,如果第一步返回了nil,就尝试来到这里
// 调用方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(testForwarding)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
// 返回了方法签名后,会来到调用 - (void)forwardInvocation:(NSInvocation *)anInvocation 方法
}
return [super methodSignatureForSelector:aSelector];
}
// NSInvocation 封装了一个方法调用,包括:方法调用者,方法名,方法参数
// anInvocation.target
// anInvocation.selector
// [anInvocation getArgument:NULL atIndex:0]
- (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"abc--");
}
打印结果:abc–
- 比如当方法带有参数的时候,我想获取参数
- (void)testForwarding:(int)number;
Teacher *teacher = [[Teacher alloc] init];
NSLog(@"--%s",@encode(int)); // 打印: --i // 可以知道int类型的参数为i
[teacher testForwarding:10];
修改方法签名
// 第二步,如果第一步返回了nil,就尝试来到这里
// 调用方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(testForwarding:)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:i"];
// 返回了方法签名后,会来到调用 - (void)forwardInvocation:(NSInvocation *)anInvocation 方法
}
return [super methodSignatureForSelector:aSelector];
}
获取参数
// NSInvocation 封装了一个方法调用,包括:方法调用者,方法名,方法参数
// anInvocation.target
// anInvocation.selector
// [anInvocation getArgument:NULL atIndex:0]
- (void)forwardInvocation:(NSInvocation *)anInvocation {
int parm;
[anInvocation getArgument:&parm atIndex:2];
// 因为方法 - (void)testForwarding:(int)number;
// 带有2个隐藏参数,所以 int 是第三个参数,index的值为2
NSLog(@"abc--%d",parm);
}
打印结果:abc–10
成功获得参数
- 补充:生成方法签名也可以这样写
// 第二步,如果第一步返回了nil,就尝试来到这里
// 调用方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(testForwarding:)) {
return [[[Dog alloc] init] methodSignatureForSelector:aSelector];
// return [NSMethodSignature signatureWithObjCTypes:"v@:i"];
// 返回了方法签名后,会来到调用 - (void)forwardInvocation:(NSInvocation *)anInvocation 方法
}
return [super methodSignatureForSelector:aSelector];
}
* 类方法消息转发
+ (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return [Dog class];
}
return [super forwardingTargetForSelector:aSelector];
}
-
tips : 要先敲减号开头的方法,然后再改成加号开头的方法
-
当forwardingTargetForSelector返回空的时候,另外两个方法实现
+ (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
// return [Dog class];
return nil;
}
return [super forwardingTargetForSelector:aSelector];
}
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
+ (void)forwardInvocation:(NSInvocation *)anInvocation {
NSLog(@"123");
}
打印结果: 123
- 把类对象方法,转发给对象方法
#import "Dog.h"
@implementation Dog
- (void)testForwarding:(int)number {
NSLog(@"Dog-testForwarding");
}
+ (void)test {
NSLog(@"Dog-test");
}
- (void)test {
NSLog(@"对象方法:Dog-test");
}
@end
+ (id)forwardingTargetForSelector:(SEL)aSelector {
if (aSelector == @selector(test)) {
// return [Dog class];
return [[Dog alloc] init];
}
return [super forwardingTargetForSelector:aSelector];
}
打印结果:对象方法:Dog-test
@synthesize 关键字,很久以前的xcode需要手动写@synthesize age = _age;
作用:生成带下划线的变量名
@property(nonatomic, assign) int age;
@synthesize age;
- (void)setAge:(int)bb {
age = bb; // 不会有_age
}
@synthesize age = _age;
- (void)setAge:(int)age {
_age = age; // 有_age;
}
@dynamic 关键字:告诉编译器不要实现set、get方法;
注意:有声明,没有实现
@dynamic age;