5.4 SEL类型
1 id cattle[3];
2 SEL say;
3 SEL skin;
其中id cattle[3]定义了一个数组用于存储Cattle或者Bull对象。这一行代码估计大家都很熟悉,笔者就不赘述了。像这样的传统的数组并不能完全满足我们的需求,当我们需要做诸如追加,删除等操作的时候,会很不方便。在随后的章节里面笔者将要向大家介绍传统数组的替代解决方案NSArray。
上一段代码的第二行和第三行是本节所关注的,就是SEL类型。Objective-C在编译的时候,会根据方法的名字(包括参数序列),生成一个用 来区分这个方法的唯一的一个ID,这个ID就是SEL类型的。我们需要注意的是,只要方法的名字(包括参数序列)相同,那么它们的ID都是相同的。就是 说,不管是超类还是子类,不管是有没有超类和子类的关系,只要名字相同那么ID就是一样的。除了函数名字和ID,编译器当然还要把方法编译成为机器可以执 行的代码,这样,在一个编译好的类里面,就产生了如下图所示方法的表格示意图(本构造属于笔者推测,没有得到官方证实,所以图5-2为示意图仅供参考,我们可以暂时认为是这样的)。
图5-2,方法的表格示意图
请注意setSkinColor后面有一个冒号,因为它是带参数的。由于存在这样的一个表格,所以在程序执行的时候,我们可以方便的通过方法的名字,获取到方法的ID也就是我们所说的SEL,反之亦然。具体的使用方法如下:
1 SEL 变量名 = @selector(方法名字);
2 SEL 变量名 = NSSelectorFromString(方法名字的字符串);
3 NSString *变量名 = NSStringFromSelector(SEL参数);
其中第1行是直接在程序里面写上方法的名字,第2行是写上方法名字的字符串,第3行是通过SEL变量获得方法的名字。我们得到了SEL变量之后,可以通过下面的调用来给一个对象发送消息:
[对象 performSelector:SEL变量 withObject:参数1 withObject:参数2];
这样的机制大大的增加了我们的程序的灵活性,我们可以通过给一个方法传递SEL参数,让这个方法动态的执行某一个方法;我们也可以通过配置文件指定需要执行的方法,程序读取配置文件之后把方法的字符串翻译成为SEL变量然后给相应的对象发送这个消息。
从效率的角度上来说,执行的时候不是通过方法名字而是方法ID也就是一个整数来查找方法,由于整数的查找和匹配比字符串要快得多,所以这样可以在某种程度上提高执行的效率。
下面再来看看苹果官方文档对于消息机制的说明:
objc_msgSend函数
在Objective-C中,消息是直到运行的时候才和方法实现绑定的。编译器会把一个消息表达式,
[receiver message]
转换成一个对消息函数objc_msgSend的调用。该函数有两个主要参数:消息接收者和消息对应的方法名字——也就是方法选标:
objc_msgSend(receiver, selector)
同时接收消息中的任意数目的参数:
objc_msgSend(receiver, selector, arg1, arg2, ...)
该消息函数做了动态绑定所需要的一切:
它首先找到选标所对应的方法实现。因为不同的类对同一方法可能会有不同的实现,所以找到的方法实现依赖于消息接收者的类型。
然后将消息接收者对象(指向消息接收者对象的指针)以及方法中指定的参数传给找到的方法实现。
最后,将方法实现的返回值作为该函数的返回值返回。
注意:编译器将自动插入调用该消息函数的代码。您无须在代码中显示调用该消息函数。
消息机制的关键在于编译器为类和对象生成的结构。每个类的结构中至少包括两个基本元素:
指向父类的指针。
类的方法表。方法表将方法选标和该类的方法实现的地址关联起来。例如,setOrigin::的方法选标和setOrigin::的方法实现的地址关联,display 的方法选标和display的方法实现的地址关联,等等。
当新的对象被创建时,其内存同时被分配,实例变量也同时被初始化。对象的第一个实例变量是一个指向该对象的类结构的指针,叫做isa。通过该指针,对象可以访问它对应的类以及相应的父类。
注意:尽管严格来说这并不是Obective-C语言的一部分,但是在Objective-C运行时系统中对象需要有isa指针。对象和结构体struct objc_object(在objc/objc.h中定义)必须“一致”。然而,您很少需要创建您自己的根对象,因为从NSObject或者NSProxy继承的对象都自动包括isa变量。
当对象收到消息时,消息函数首先根据该对象的isa指针找到该对象所对应的类的方法表,并从表中寻找该消息对应的方法选标。如果找不到,objc_msgSend将继续从父类中寻找,直到NSObject类。一旦找到了方法选标, objc_msgSend则以消息接收者对象为参数调用,调用该选标对应的方法实现。
这就是在运行时系统中选择方法实现的方式。在面向对象编程中,一般称作方法和消息动态绑定的过程。
为了加快消息的处理过程,运行时系统通常会将使用过的方法选标和方法实现的地址放入缓存中。每个类都有一个独立的缓存,同时包括继承的方法和在该类中定义的方法。消息函数会首先检查消息接收者对象对应的类的缓存(理论上,如果一个方法被使用过一次,那么它很可能被再次使用)。如果在缓存中已经有了需要的方法选标,则消息仅仅比函数调用慢一点点。如果程序运行了足够长的时间,几乎每个消息都能在缓存中找到方法实现。程序运行时,缓存也将随着新的消息的增加而增加。
注:方法选标 即SEL 类型值 也就是@selector(methodname)获取的一个结构变量
下面是我从cocoachina.com上下载的关于objc_msgSend()函数的用法代码(省去了头文件)
//test.m
//
// test.m
// SelectorTest
//
// Created by Zenny Chen on 09-7-20.
// Copyright 2009 GreenGames Studio. All rights reserved.
//
#import "test.h"
static NSString *test1Str = @"I\'m No.1";
static NSString *test2Str = @"I\'m No.2";
@implementation Test1
- (id)init
{
value = 0;
return self;
}
- (void)setValue : (NSInteger)val
{
value = val;
}
- (NSInteger)getValue
{
return value;
}
+ (void)setValue : (NSString*)str
{
if(str != nil)
test1Str = [test1Str stringByAppendingString:[@"\n" stringByAppendingString:str]];
}
+ (NSString*)getValue
{
return test1Str;
}
@end
@implementation Test2
- (id)init
{
value = 0;
return self;
}
- (void)setValue : (NSInteger)val
{
value = val;
}
- (NSInteger)getValue
{
return value;
}
+ (void)setValue : (NSString*)str
{
if(str != nil)
test2Str = [test2Str stringByAppendingString:[@"\n" stringByAppendingString:str]];
}
+ (NSString*)getValue
{
return test2Str;
}
@end
//SelectorTest.m
#import <Foundation/Foundation.h>
#import "test.h"
#import "runtime.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// insert code here...
SEL selSetter = @selector(setValue:);
SEL selGetter = @selector(getValue);
NSLog(@"The setter method name is: %@", NSStringFromSelector(selSetter));
NSLog(@"The getter method name is: %@", NSStringFromSelector(selGetter));
// Send a message to an instance
Test1 *t1 = [[Test1 alloc] init];
objc_msgSend(t1, selSetter, 100); // equal to [t1 setValue:100];
NSLog(@"The t1 value is: %d", objc_msgSend(t1, selGetter)); // equal to [t1 getValue];
Test2 *t2 = [[Test2 alloc] init];
objc_msgSend(t2, selSetter, -100);
NSLog(@"The t2 value is: %d", objc_msgSend(t2, selGetter));
// Send a message to a class
objc_msgSend(objc_lookUpClass("Test1"), selSetter, @"Ah, yes!"); // equal to [Test1 setValue:@"Ah, yes!"];
objc_msgSend(objc_lookUpClass("Test2"), selSetter, @"Perfect!");
NSLog(@"The Test1 result is: %@", objc_msgSend(objc_lookUpClass("Test1"), selGetter));
NSLog(@"The Test2 result is: %@", objc_msgSend(objc_lookUpClass("Test2"), selGetter));
// Other runtime APIs
NSLog(@"The class name is: %s", class_getName(((id)t1)->isa));
Method test1Method = class_getClassMethod(object_getClass(t1), selSetter);
NSLog(@"The method name is: %@", NSStringFromSelector(method_getName(test1Method)));
//现在使用objective-C/C++方法来处理选择符
[t1 performSelector:selSetter withObject:(id)111]; //注意,这里不能使用[NSNumber numberWithInteger:111]
[t2 performSelector:selSetter withObject:(id)222];
[Test1 performSelector:selSetter withObject:@"Mama Miya!"];
[Test2 performSelector:selSetter withObject:@"That's what I needed"];
NSLog(@"The t1 value now is: %d", [t1 performSelector:selGetter]);
NSLog(@"The t2 value now is: %d", [t2 performSelector:selGetter]);
NSLog(@"Test1 value is: %@", [Test1 performSelector:selGetter]);
NSLog(@"Test2 value is: %@", [Test2 performSelector:selGetter]);
//注意:(id)performSelector只有三种形式:无参数、带有一个参数、带有两个参数,并且参数类型均为id类型。
//因此,若当所需参数多时可以使用数组传入,如果多个参数类型又不同,那么要么改进设计方法,要么使用运行时API进行处理。(可以传递自己设计的对象)
[t1 release];
[pool drain];
return 0;
}
// please refer to the runtime refernece manual from Apple inc.
// The runtime APIs referred to here: objc_msgSend, objc_lookUpClass, class_getName, method_getName, class_getClassMethod
分享到:
相关推荐
`SEL`表示一个方法选择器,是一个代表方法名称的标识符。`SEL`可以通过`@selector()`在编译时获取,或在运行时通过字符串动态转换。这种灵活性使得动态方法调用成为可能,增强了Objective-C的动态性。 在Objective-...
Objective-C通过方法选择器(selector)实现多态,同一个消息可以被不同的对象以不同方式处理。 5. 消息传递:Objective-C的核心是消息传递机制。当你调用一个对象的方法时,实际上是向该对象发送一条消息。对象...
5. **方法定义**:Objective-C的方法定义可能包括选择器(selector)、参数列表和方法体。比如,`- (FractionDemo *)add:(FractionDemo *)otherFraction`是一个加法操作的方法。 6. **协议(Protocol)**:...
- **选择器(Selector)**:Objective-C中的函数通常通过选择器来标识,它是方法名的表示形式。 - **实例变量与属性(Property)**:实例变量存储对象的数据,而属性提供了访问这些数据的封装机制,包括内存管理、...
Objective-C通过方法选择器(selector)和动态类型实现多态。 10. **块(Blocks)**:Objective-C中的块是一种内联函数,可以捕获和存储执行上下文中的变量。它们常用于异步操作的回调,或者在GCD(Grand Central ...
消息由接收者、选择器(selector)和参数组成。例如,`[object performSelector:@selector(method)];` 表示向`object`发送一个名为`method`的消息。 4. **协议(Protocols)**: Objective-C的协议类似于Java或C#...
接下来,书中会讲解Objective-C的语法,如选择器(selector)、协议(protocol)、分类(category)和扩展(extension)。选择器是Objective-C中表示方法的唯一标识符,用于消息的发送。协议定义了一组可选的方法,...
理解消息传递机制,包括方法选择器(selector)、动态绑定(dynamically bound)和运行时系统(runtime system)的功能,对于编写高效代码至关重要。 3. **协议与类别**: - 协议在Objective-C中扮演着接口的角色...
理解`[receiver message]`的格式,以及方法选择器(selector)的概念。 4. **协议(Protocol)**:Objective-C中的协议类似于Java或C#的接口,定义了一组可选的方法。学习如何使用协议来实现多继承的效果,以及在...
1. **选择器**:Objective-C中的方法通常通过选择器来调用,例如`[object performSelector:@selector(methodName)]`。 2. **协议**:Objective-C支持协议,即接口的概念,用于定义一组方法,其他类必须实现这些方法...
Class 类型、选择器和函数指针是 Objective-C 中非常重要的概念。Class 类型允许你访问类的元信息,而选择器则是用于发送消息的标识符。函数指针则是一种指向函数的指针类型,这对于实现某些设计模式非常有用。 ###...
Objective-C使用选择器来动态调用方法。Swift提供了`Selector`类型,可以用于调用Objective-C的方法。这在实现某些Objective-C设计模式时特别有用。 ##### 使用Objective-C特性编写Swift类 **继承Objective-C的类*...
消息由接收者、选择器(selector,表示方法名)和可能的参数组成。 4. 动态类型(Dynamic Typing):Objective-C支持运行时类型检查,使得在编写代码时不必预先知道对象的确切类型。 二、Objective-C的关键特性 1...
- **方法**:在Objective-C中,方法是对象发送消息的结果,通过选择器(selector)来调用。 #### 三、源代码组织 Objective-C项目通常包括`.h`和`.m`两种文件类型: - **.h文件**:包含接口声明(即类声明),...
- **选择器**:选择器是用来标识特定方法的标签,例如: ```objective-c SEL selector = @selector(sayHello); [person performSelector:selector]; ``` ##### 2.3 属性与合成 - **属性声明**:使用`@property`...
4. 方法(Method):对象可以响应的消息,由选择器(Selector)和实现(Implementation)组成。 5. 属性(Property):用来封装对象的变量,提供了自动内存管理、访问控制和KVC(Key-Value Coding)支持。 三、...
2. **选择器(Selector)**:选择器是用于标识方法名的指针,可以在运行时动态调用方法。 3. **消息发送**:Objective-C中的方法调用实际上是向对象发送消息的过程,例如`[object method];`。 4. **自动引用计数...
此外,书中还会涵盖选择器(selector)和协议(protocol)等关键概念,这些都是Objective-C中实现多态和接口定义的重要组成部分。 在面向对象特性之外,Objective-C还引入了Category和Extension,这两种机制允许...