Objective

在这之前, 我们回想一下, 我们知道实现我们所需要的功能, 就要调用某些方法, 那么这些方法又是怎么样调用的呢? 原理是怎么样的? 让我们一起来探讨一下看看~~



例子:

#import <Foundation/Foundation.h>

@interface Person : NSObject
- (void)test;
@end

@implementation Person
- (void)test
{
    NSLog(@"调用了test方法");
}
@end

int main()
{
    Person *p = [[Person alloc]init];
    [p test];
    return 0;
}

上面这个例子就是我们平常所写的, 那么方法调用的原理是什么??



1. 把test包装成SEL类型的数据.

2. 根据SEL数据找到对应方法的地址.

3. 根据方法地址调用对应的方法.



这就是方法调用的原理, 在我们所写的每一个方法中, 都会有相应的方法地址, 而SEL的任务就是去找对应方法的地址, 那么示意图是怎么样的呢? 让我们一起来看看:





那么根据这个, 我们就可以猜猜看, 在调用方法的时候是不是有多种方式呢, 继续往下看:

#import <Foundation/Foundation.h>

@interface Person : NSObject
- (void)test2;
@end

@implementation Person
- (void)test2
{
    NSLog(@"调用了test2方法.");
}
@end

int main()
{
    Person *p = [[Person alloc]init];
    [p test2];

    [p performSelector:@selector(test2)];
    return 0;
}

结果:

2015-01-24 21:33:08.046 09-SEL类型[830:48415] 调用了test2方法.
2015-01-24 21:33:08.047 09-SEL类型[830:48415] 调用了test2方法.


事实证明, 第二种方法也是可以的, 这种是属于间接调用, 那如果添加一个参数呢, 同样也是可以的.

#import <Foundation/Foundation.h>

@interface Person : NSObject
+ (void)test;
- (void)test2;
- (void)test3:(NSString *)str;
@end

@implementation Person
+ (void)test
{
    NSLog(@"调用了test方法.");
}

- (void)test2
{
    NSLog(@"调用了test2方法.");
}

- (void)test3:(NSString *)str
{
    NSLog(@"test3------%@", str);
}
@end

int main() 
{

    Person *p = [[Person alloc]init];
        
    [p test2];
        
    [p performSelector:@selector(test2)];
    
    [p performSelector:@selector(test3:) withObject:@"ABCDEFG"];

    return 0;
}

输出的结果:

2015-01-24 21:35:58.273 09-SEL类型[844:49833] 调用了test2方法.
2015-01-24 21:35:58.274 09-SEL类型[844:49833] 调用了test2方法.
2015-01-24 21:35:58.274 09-SEL类型[844:49833] test3------ABCDEFG




还有几种方法, 比如把字符串转成SEL类型:

int main() 
{

    Person *p = [[Person alloc]init];
        
    NSString *name = @"test2";

    SEl s = NSSelectorFromString(name);

    [p performSelector:s];

    return 0;
}

这样子也是可以调用的.




再补充一个知识点, 其实每一个方法内部都隐藏着一个SEL的数据, 而且是系统自带的, 叫做_cmd, 下面让我们来看看:

#import <Foundation/Foundation.h>

@interface Person : NSObject
+ (void)test;
- (void)test2;
@end

@implementation Person
+ (void)test
{
    NSLog(@"调用了test方法.");
}

- (void)test2
{
    NSString *str = NSStringFromSelector(_cmd);
    NSLog(@"调用了test2方法------%@.", str);
}
@end

int main() 
{

    Person *p = [[Person alloc]init];
    
    [p test2];    

    return 0;
}

结果:

2015-01-24 22:10:44.445 09-SEL类型[920:59424] 调用了test2方法---test2.

看到结果之后, 我们就知道了, 在哪个方法里调用_cmd, 那么这个_cmd就指向谁, 也就是代表着当前的这个方法.



注意, 千万不要在方法里面写一句这样子的代码:

[self performSelector:_cmd];

这样子会引发死循环的, 所以千万不能这么写.





总结一下: SEL其实是对方法的一种包装, 将方法包装成一个SEL类型的数据, 去找对应的方法地址, 找到该方法的地址, 就可以调用该方法了.



其实也可以这么说, SEL其实也是发送消息的一种, 原因就看上面的例子, 自己慢慢琢磨吧.




好了, 今天我们就讲到这里, 下次我们继续~~~

原文地址:https://www.cnblogs.com/iOSCain/p/4282831.html