------- android培训、java培训、期待与您交流!
=========官方文档翻译=========
在OC中,selector有两层含义。
1、当selector在源代码中被用来指向一个对象的时候,selector可以仅仅指这个方法的名称。
2、代码编译的时候会生成一个唯一的标示符,selector也可以指向该标示符。
selectors的编译类型为SEL。所有有同样名称的方法都有同样的selector。针对某个对象(object),你可以使用selector去调用它的方法。这给Cocoa的目标-行为设计模式(the target-action design pattern,注:关于这部分在该篇API的最后进行了说明)提供了执行的基础
Methods and Selectors
为了提高效率,在编译规则(compiled code)中一个完整的ASCII名称不被作为方法的selectors。取而代之的方法是,编译器将每一个方法的名称写到一个table中,然后跟一个唯一的标示符配对,该标示符在运行过程中会代表一个具体的方法。运行系统(runtime system)确保每一个标示符都是唯一的:没有两个selector是相同的,而且所有有同样名称的方法有同样的selector
SEL and @selector
selector编译后被指定到一种特殊的类型——SEL来与其它类型进行区分。有效的selector永远不会为0.你必须让系统给方法分配SEL标示符,自行任意分配的标示符是无效的。
你应该使用@selector()指令将方法名传递给编译的selector,而不是直接使用一个方法的全名。比如,下面的方法可以获得setWidth:height: 方法的selector,并且分配给setWidthHeight变量:
SEL setWidthHeight;
setWidthHeight = @selector(setWidth:height:);
最有效的的方式是在编译的时候调用@selector()指令给SEL变量赋值。但在有些情况下,你可能需要在运行时候将字符串转换给某个selector。你可以使用NSSelectorFromStringn函数完成这项工作:
setWidthHeight = NSSelectorFromString(aBuffer);
反着转换也是可行的。NSStringFromSelector函数可以返回某个selector的方法的名称:
NSString *method;
method = NSStringFromSelector(setWidthHeight);
Methods and Selectors
selector编译后可以识别方法名称,但不实现方法(not method implementations)。比如,对于一个类而言,它的display方法和其它同样定义了display方法的类有相同的selector。对于动态绑定和多态性而言这是必不可少的,它可以让你给不同类的接收器发送相同的方法。如果每一个执行方法都有一个selector,那么发送这条信息就跟调用了一个函数回调(function call)没什么区别了。
具有相同名称的类方法和实例方法被分配了相同的selector,单因为他们属于不同的领域,这两者之间也不会产生混淆。一个类能定义display方法,附加给一个实例方法。
Method Return and Parameter Types
通告程序(messaging routine)只能通过selector访问方法实体(method implementation),所以它用同样的selector处理所有方法 (注:这句话不理解,附原文so it treats all methods with the same selector alike)。通告程序(it)可以通过selector辨别一个方法的返回类型、以及参数的数据类型。因此,除非通告(message)是发送给静态类型的接收器,否则对于动态绑定接收器,会要求所有有同样名称方法的实例页有同样的返回类型和参数类型。(对于该规则,静态类型的接收器属于例外的原因是编译器能从类的类型了解该方法实体。)
虽然相同名称的类方法和实例方法是使用同样的selector代表的,它们可以有不同的参数类型和返回值类型。
Varying the Message at Runtime
NSObject协议中定义的 performSelector:, performSelector:withObject:,和 performSelector:withObject:withObject:方法使用SEL标示符作为它们的初始化参数。所有这三个方法(注:我很奇怪为什么是三个,但是原文就是写的三个...)直接映射到通告功能(massaging function)。例如:
[friend performSelector: @selector(gossipAbout:)
withObject: aNeighbor];
相当于
[friend gossipAbout:aNeighbor];
这些方法使应用运行时改变message成为可能,就像可以改变一个接收message的object一样。Variable names can be used in both halves of a message expression:
id helper = getTheReceiver();
SEL request = getTheSelector();
[helper performSelector:request];
在这个例子中,接收器helper在运行时候进行选择(通过一个假设的getTheReceiver函数),方法接收器页要求在运行时执行request(同样通过一个假设的getTheSelector函数)
Note:performSelector: 方法和它其它同伴的方法都返回一个类型为id的对象,如果当前执行的方法返回一个不同的类型,它应当被转换为适当的类型。(但,转换不适用于所有类型,方法应当返回一个指针或者一个指针兼容的类型)
The Target-Action Design Pattern
.......
========================
方法的存储位置:
>每个类的方法列表都存储在类对象中
>每个方法都有一个与之对应的SEL类型的对象
>根据一个SEL对象就可以找到方法的地址,进而调用方法
>SEL类型的定义:typedef struct objc selector *SEL;
//SEL 其实是对方法的一种包装,将方法包装成一个SEL类型的数据,去找对应的方法地址,找到方法地址就可以调用方法
传说中的消息是发一个SEL类型的数据,根据其找对应的地址调用方法
以后可以用一些高级特性,直接拿到方法地址,运行,不需要SEL包装
Person.h文件中:
#import <Foundation/Foundation.h>
@interface Person : NSObject
+ (void)test;
- (void)test;
- (void)test3:(NSString *)abc;
@end
Person.m文件中:
#import "Person.h"
@implementation Person
+ (void)test{
NSLog(@"-------test-------");
}
- (void)test2{
//_cmd==@selector(test2);
NSString *str =NSStringFromSelector( _cmd)//============每个方法都有
NSLog(@"-------test2------%@",str);
}
- (void)test3:(NSString *)abc{
NSLog(@"-------tes3------%@",abc);
}
@end
main.m文件中:
#import <Foundation/Foundation.h>
#import "Person.h"
int main(){//测试几种调用test的方式
Person *p = [[Person alloc] init];
[p test2]; //使用_cmd后打印出当前方法,即:test2
/*
1.把test2包装成SEL类型的数据
2.根据SEL数据找到对应的方法地址
3.根据方法地址调用对应的方法
*/
//利用selector间接调用test2方法
[p performSelector:@selector(test2)]; //和 [p test2]相同
[p test3:@"123"];
//使用SEL:
NSString *name = @"test2";
SEL s2 = NSSelectorFromString(name);//把一个字符串转成SEL类型数据
NSString *str = NSStringFromSelector(@selector(test));//把SEL对象转为NSString对象
[p performSelector:s2];
SEL s = @selector(test3:);
[p performSelector:s withObject:@"456"];
[p performSelector: @selector(test3:) withObject:@"123"];//(test:)才是方法名,不能写成test
return 0;
}