iOS开发 方法签名

1. 什么是方法签名?

Java中的方法签名是由方法名称和一个参数列表(方法的参数的顺序和类型)组成, 不包含方法的返回值.
iOS中的方法签名是通过NSMethodSignature实现的, 下面我们看看NSMethodSignature的常用方法和属性

1.1 NSMethodSignature自己的方法

//手动初始化方法
+ (nullable NSMethodSignature *)signatureWithObjCTypes:(const char *)types;

使用:

NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:"v@:"];

signatureWithObjCTypes的参数可查看官方文档的相关介绍

问题----------------

我们上面说方法的签名是由方法名称和一个参数列表(方法的参数的顺序和类型)组成, 不包含方法的返回值.
但iOS中的方法签名似乎不一样, 不涉及方法名, 但是包含方法的返回值类型(void), 参数类型(obj, SEL), 真的是这样么??
我们需要保留这个问题, 在实际使用过程中或许能够明白这个问题.

1.2 NSObject中的方法

除了NSMethodSignature类中包含创建NSMethodSignature对象的方法, NSObject中也包含获取NSMethodSignature对象的方法

//1. 获取类方法或者实例方法签名, 无论是对象还是类对象都能调用该方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
//2. 只能获取实例方法签名
+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector

值得注意的是, NSObject中的方法, NSObject的子类都可以调用, 所以一般有三种情况

  1. 从类中获取类方法签名
  2. 从类中获取实例方法签名
  3. 从实例中获取实例方法签名
    但是不能从实例中获取类方法签名, 因为类方法是存放在元类中的, 实例只能获取实例对应的类对象中的方法

1.2.1 从类中获取类方法签名实例方法签名

    NSMethodSignature *s1 = [NSString methodSignatureForSelector:@selector(alloc)];
    NSMethodSignature *s2 = [NSString methodSignatureForSelector:@selector(init)];
    NSLog(@"--s1:%@",s1);
    NSLog(@"--s2:%@",s2);

结果:

屏幕快照 2018-08-16 上午9.29.21.png

我们可以发现, 从不同的方法获取到的方法签名, 为什么是一样的?
因为方法虽然不同, 但是方法的签名是一样的(同一个 NSMethodSignature), 即方法的 返回值类型参数类型是一样的

1.2.2 从实例对象中可获取实例方法签名, 不能获取类方法签名

    NSString *str = @"string...";
    NSMethodSignature *s3 = [str methodSignatureForSelector:@selector(alloc)];
    NSMethodSignature *s4 = [str methodSignatureForSelector:@selector(init)];
    NSLog(@"--s3:%@",s3);
    NSLog(@"--s4:%@",s4);

结果:


屏幕快照 2018-08-16 上午9.48.33.png

1.2.3 instanceMethodSignatureForSelector只能获取实例方法的签名

NSMethodSignature *s5 = [NSString instanceMethodSignatureForSelector:@selector(alloc)];
NSMethodSignature *s6 = [NSString instanceMethodSignatureForSelector:@selector(init)];
NSLog(@"--s5:%@",s5);
NSLog(@"--s6:%@",s6);

结果:


屏幕快照 2018-08-16 上午9.53.12.png


2. 如何使用方法签名

- (void)signatureTest {
    //1. 创建方法签名
    NSMethodSignature *s9 = [NSMethodSignature signatureWithObjCTypes:"v@:ii"];
    //2. 根据方法签名创建一个NSInvocation对象
    NSInvocation *invo = [NSInvocation invocationWithMethodSignature:s9];
    //3. 为NSInvocation对象设置接收消息的对象
    [invo setTarget:self];
    //4. 为NSInvocation对象设置发送的消息
    [invo setSelector:@selector(test11:)];
    //5. 为NSInvocation对象设置一个新的参数
    NSInteger a = 1;
    [invo setArgument:&a atIndex:2];
    //6. 调用NSInvocation
    [invo invoke];
}

- (void)test11:(int )a{
    NSLog(@"-----:%d",a);
}

结果:

2018-08-16 11:19:39.493722+0800 RuntimeNew[4527:236471] -----:1

解释:

  1. 我们一开始创建的方法签名是有4个参数 @ : i i, 除了前两个是每个方法都有的, 我们又添加了两个int类型的参数, 但是我们的方法- (void)test11:(int )a中并只有一个参数, 依然可以执行, 说明签名函数的参数数量大于被调用的函数参数数量时, 也是可以正常调用的.


3. iOS中的方法签名是否包含方法名?

我们可以从两方面去看待这个问题

3.1 我们可以从上面signatureTest方法的第四步: 为NSInvocation对象设置发送的消息

[invo setSelector:@selector(test11:)];

我们在已经拥有方法签名的情况下, 还是需要添加Selector, 说明我们还是需要方法名称的, 而不仅仅是返回值类型参数类型

3.2 这一点, 我们在Runtime的方法转发机制中也有体现

- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(testMethod:)];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    return YES;//返回YES,进入下一步转发
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    return nil;//返回nil,进入下一步转发
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if ([NSStringFromSelector(aSelector) isEqualToString:@"testMethod:"]) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    }
    return [super methodSignatureForSelector:aSelector];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation {
    SEL sel = anInvocation.selector;
    Person *p = [Person new];
    if([p respondsToSelector:sel]) {
        NSString *str = @"Lucy";
        [anInvocation setArgument:&str atIndex:2];
        [anInvocation invokeWithTarget:p];
    }
    else {
        [self doesNotRecognizeSelector:sel];
    }
}
  1. 我们希望self执行testMethod, 但是self并没有实现该方法, 所以我们要一步一步去找
  2. resolveInstanceMethod:返回YES, 意味着我们没有为testMethod添加函数实现, 执行下一步forwardingTargetForSelector
  3. forwardingTargetForSelector返回nil, 意味着我们没有为该方法提供备用接收者, 所以只能继续执行下一步, 通过方法签名和NSInvocation来实现完整的消息转发
  4. methodSignatureForSelector拿到方法签名---在forwardInvocation指定一个对象, 判断该对象是否实现了此方法. 实现了, 则执行; 未实现, 则调用doesNotRecognizeSelector方法

问题就出在这一步, 如果我们iOS中的方法签名只看方法的返回值类型参数类型, 我们知道, 一个类中, 有很多方法的返回值类型参数类型是相同的, 这样会出现什么情况? 调用多个方法?
所以我们必定是要把方法名作为方法签名的重要一部分的.实际上, 我们在forwardInvocation方法中是拿到了方法名的, 我们可以输出一下看看

SEL sel = anInvocation.selector;
NSLog(@"%s",anInvocation.selector);
result

综上, iOS中的方法签名是包含方法名的, 只是有时候不是那么明显

你可能感兴趣的:(iOS开发 方法签名)