iOS底层系列之<17>--Runtime(五)消息机制之第三阶段:消息转发

第三阶段:消息转发

  • 图解消息转发流程

在这里插入图片描述

  • 代码展示
// 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;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值