Selector & SEL

一 、Selector(选择器)简介

选择器是用来选择一个方法来为一个对象 执行的名称,或是在编译源代码时替换该名称的 唯一标识符的名称。一个选择器本身不做任何事情。它简单地识别了一种方法。唯一使选择器的方法名称不同于普通字符串,编译器确保选择器是独特的。使一个选择器有用的是(在运行时)它就像一个 动态函数指针,对于一个给定的名称,自动指向一个方法的实现,适用于任何一个它所使用的类。

假设你有一个run方法选择器和类Dog、Student。选择器可以使用每一个类的一个实例来调用它的运行方法,即使每个类的实现可能是不同的。

二、获取一个SEL类型的Selector

编译的选择器的类型是SEL。有两种常见的方法来获得选择器:

(1)编译时,通过编译器指令 @selector 来获取.

  SEL aSelector = @selector(methodName);

(2)运行时,通过字符串来获取一个方法名 NSSelectorFromString

  SEL aSelector = NSSelectorFromString(@”methodName”);

三、Objective-C的对象会被编译成结构体(Structure)?

例如,我们现在写一个最简单的类,里面只有int i这个成员变量:

@interface SelectorClass : NSObject {
    int I;
}
@end

会被编译为:

typedef struct {
    int a;
} SelectorClass;

因为Objective-C的对象本质就是C的结构体,所以当我们建立一个Objective-C的对象之后,我们就可以把这个对象当做调用C的结构体的调用:

SelectorClass *obj = [[SelectorClass alloc] init];
obj->a = 10;

四、OC方法调用详解 - Runtime做什么

在执行的时候,runtime会为每一个类准备好一个虚拟表(专业术语叫virtual table),虚拟表里面会以每一个字符串当做key,每一个key对应到C function的指定的位置。Runtime里面,把实现C function定义成IMP(具体的方法地址)这个类型(type);至于拿来当做key的字符串,就叫做selector,类型(type)定义成SEL(方法名称的描述),然后我们就可以使用@selector关键字建立selector。

Selector示意图

其实SEL就是C语言字符串,我们可以来写一个简单的程序验证一下:

NSLog(@"%s", (char *)(@selector(doSomething)));
顺利印出「doSomething」这个 C 字串

每次对一个对象调用某个方法,runtime在做的事情,就是把方法的名称作为字符串,寻找与字符串符合的C function实现,然后执行。也就是说,以下三件事是一样的:
我们可以直接要求某个对象执行某个方法:

[myObject doSomthing];

或者是通过performSelector:调用。performSelector:是NSObject的方法,而Cocoa Framework中所有的对象都继承自NSObject,所以每一个对象都可以调用这个方法。

[myObject performSelector:@selector(doSomething)];

我们可以把以上这个程序看做一句话【我们正在吃饭】,使用performSelector:就像是【我们正在进行一个吃饭的动作】。
而其实,底层执行的却是objc_msgSend。

objc_msgSend(myObject, @selector(doSomething), NULL);

我们就有机会在程序已经执行的时候加入,继续对某个类加入新方法,一个类已经存在某个方法,也可以在runtime(运行时)用别的实现换掉,一般来说,我们会用Category做这件事。

如果我们不想用category,而想要自己动手写点程序,手动将这些方法加入到某个类中,我们就可以这么写。首先先声明一个C function,至少要有两个参数,第一个参数是执行method的对象,第二个参数是selector,像是这样:

void myMethodIMP(id self, SEL _cmd) {
    doSomething();
}

接下来可以调用class_addMethod加入selector与实现的封装。

“#import <objc/runtime.h>
// 此处省略。。。。。
class_addMethod( [MyClass class] , @selector(myMethod), (IMP)myMethodIMP, "v@:" );

接下来就可以调用了:

MyClass *myObject = [[MyClass alloc] init];
[myObject myMethod];

五、Selector 有何用处呢?

具体查看 -- iOS套路面试题之Selector

  • Target/Action 模式

  • 检查方法是否存在?
    最常见的问题就是顾忌向下兼容的问题:比如不同的iOS版本间,高版本使用的方法,低版本可能没有。这样的话可能就要做检查。
    检查某个对象是否运行了某个方法,只要调用respondsToSelector:即可:

BOOL scale = 1.0;
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
    scale = [UIScreen mainScreen].scale;
}
  • Timer是什么?

  • 接收 NSNotification?
    如果我们要接收NSNotification,我们也要在开始的时候注册通知,指定由哪一个 selector 处理通知。

  • 如何在某个线程中执行方法?

  • Array排序

  • 代替if...else与switch…case

  • 呼叫私有API

  • Selector是 Objective-C 中所有的所有的开始

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容