@selector()选择器在我们的平常开发中用到很多,但是很多人并不清楚他的含义或者原理,还有用选择器来触发一个方法,到底是怎么的个流程,我们今天就来说一说@selector()选择器和OC中的方法调用。本篇涉及一些runtime的知识,可以提前补充一些runtime的知识。
一、基本概念
1、SEL
它是@selector()在OC中的表示,可以说是一种数据类型,在OC中的定义:typedef struct objc_selector *SEL;
2、@selector()
它是selector方法选择器,比如addObject:方法,在OC中使用@selector(addObject:)选择一个方法,本质它是一个根据方法名和参数列表生成的一个唯一的标识(更准确的说是根据方法名hash了的一个字符串值),他能唯一的代表一个方法。
在工程中,不同类中,如果方法名相同,那么这个相同方法名的SEL就是同一个,比如说A类中有-(void)addObject:(NSString *)a;
方法,B类中有-(void)addObject:(NSString *)a;
方法,那这两个方法的SEL就是同一个。
在工程中,所有的方法的SEL都是放在一个表里的,一个方法名对应一个SEL(即一个字符串)
在寻找方法的时候,不是根据方法的名字 “addObject:” 字符,而是寻找SEL里的字符“abcdefg”,在对比的时候,对比字符串只要对比字符串的地址就可以了,所以速度是很快的。
3、IMP
IMP是一个函数的地址,指向的是函数实现的地址。
它的定义如下
typedef id (*IMP)(id, SEL, ...);
第一个参数:是指向self的指针(如果是实例方法,则是类实例的内存地址;如果是类方法,则是指向元类的指针)
第二个参数:是方法选择器(selector)
接下来的参数:方法的参数列表。
前面的SEL就是为了加快找到IMP而存在的
4、Method
Method是类定义中的方法,在类的结构中有一个方法的集合的链表,里面存放的都是Method。
Method的定义:
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
在结构中可以看到有SEL和IMP,把方法选择器和IMP方法的实现实现了一一对应的关系。
二、SEL的方法
1、SEL的创建
SEL创建有三个方法
1、@selector()创建
2、NSSelectorFromString()
3、sel_registerName()
2、SEL的其他方法
使用performSelector来发送消息
[p performSelector:@selector(test)];
[self performSelector:@selector(addObject:second:) withObject:@"first" withObject:@"second"];
三、消息发送
在内存中每个类的方法都存储在类的结构空间中,每个方法都有一个与之对应的SEL类型的数据,根据一个SEL数据就可以找到对应的方法实现的地址,进而调用方法。
在runtime中方法的调用都是用消息的发送,即
id objc_msgSend(id theReceiver, SEL theSelector, …)
- theReceiver:第一个参数是消息的接受者
- theSelector:第二个参数是方法的SEL
- ...后面的参数是一些方法的参数
在调用方法的时候,比如说[person addObject:@"a"];
会转化为objc_msgSend(person, @selector(addObject:), @"a")
来发送消息,它会根据person的isa指向的Person类,在Person的类空间结构的方法的cache表中查找是否有该方法(用SEL进行对比),如果有直接返回方法的实现,如果没有会指向superClass,查找是否有该方法,一直往上寻找,如果找到则返回方法的实现,如果没有则抛出异常。
当不同的类选择同一个SEL方法执行的时候,为什么会指向不同的方法实现,最主要的就是因为objc_msSend中的theReceiver不同,根据不同的receiver在不同的类的方法列表中对应的方法实现。
注意:
1、一个类的方法的SEL和IMP是放在一个dispatch table中的,里面是一一对应的关系,所以不允许一个类有相同的SEL方法,当时可以有方法名相同的+方法和-方法,即 -(void)addObject:(NSString *)a
和 +(void)addObject:(NSString *)a
可以同时存在,以为-方法是实例方法,放在类的结构空间的方法列表中,而+方法是类方法,放在其元类的结构空间的方法列表中,所以这个不是同一张表。
2、在同一个类中,不能出现方法名相同的方法,即使参数和返回值都不同也不可以,因为类的结构中SEL和IMP是一一对应的关系,所以也不支持重载。