一 、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。
其实SEL就是C语言字符串,我们可以来写一个简单的程序验证一下:
NSLog(@"%s", (char *)(@selector(doSomething)));
每次对一个对象调用某个方法,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 有何用处呢?
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 中所有的所有的开始