通过上一章的学习,你可能很迷糊,这跟C/C++相差也太远了吧,是的,语法的表现形式上有很大的区别,但编程原理是相通的,只是表现形式上有区别而已。这一章
我们再引入一个比较新的概念:选择器,与其它语言相较,它显得很特别,但很容易理解,下面我们先看一张表:
方法名 | 方法ID | 方法地址 |
---|---|---|
testFunc | 1001 | 0x2001 |
testFunc: | 1002 | 0x2002 |
方法名 | 方法ID | 方法地址 |
---|---|---|
testFunc | 1001 | 0x2003 |
testFunc: | 1002 | 0x2004 |
程序在启动的时候就会为每个类创建一张与上面类似的表,来记录类的所有的方法名称、方法ID和方法地址,每个方法名对应的方法ID都是唯一的,即相同方法具有相同的方法ID。选择器主要就是利用方法ID的唯一性来做文章, 为了弄清楚它,我们先来看与上面表相同的两个类:
1) Test1
Test1.h
#import <Foundation/Foundation.h>
@interface Test1 : NSObject {
}
-(void) testFunc;
-(id) testFunc: (NSString*) name;
@end
Test1.m
#import "Test1.h"
@implementation Test1
- (void) testFunc
{
NSLog(@"Test1:no parameter");
}
- (id) testFunc: (NSString *) name
{
return nil;
}
@end
2) Test2
Test2.h
#import <Foundation/Foundation.h>
@interface Test2 : NSObject {
}
-(void) testFunc;
-(id) testFunc: (NSString*) name;
@end
Test2.m
#import "Test2.h"
@implementation Test2
- (void) testFunc
{
NSLog(@"Test2:no parameter");
}
- (id) testFunc: (NSString *) name
{
NSLog(@"Test2: with parameter");
return nil;
}
@end
接下来,我们在main函数中实现选择器的调用:
3) main.m
#import <Foundation/Foundation.h>
#import "Test1.h"
#import "Test2.h"
int main (int argc, const char* argv[]) {
NSAutoreleasePool* pool = [[NSAutoreleasedPool alloc] init];
SEL testFunc;
testFunc = @selector(testFunc);
SEL testFunc2;
testFunc2 = NSSelectorFromString(@"testFunc:");
NSLog(@"The testFunc name is: %@", NSStringFromSelector(testFunc));
NSLog(@"The testFunc: name is: %@", NSStringFromSelector(testFunc:));
Test1* t1 = [[Test1 alloc] init];
Test2* t2 = [[Test2 alloc] init];
[t1 performSelector:testFunc withObject: @"with parameter"];
objc_msgSend(t1, testFunc);
objc_msgSend(t2, testFunc:, @"with parameter");
[t1 release];
[t2 release];
[pool drain];
return 0;
}
由上可看出,与selector相关的函数有3个:
1) SEL 变量名 = @selector(选择器名称), 通过名称获取选择器
2) SEL 变量名 = NSSelectorFromString(@"选择器名称"); 通过方法名称字符串获取选择器
3) NSString* 变量名 = NSStringFromSelector(选择器); 获取选择器的方法名
下面我们介绍两个特殊的变量类型:
1) id
id类型与C/C++的void* 类似,它表示对象指针,但它与void*有很大的区别, objc中对象之间的调用(暂且称之为调用)是通过发送消息的方式实现的,就是在调用某个对象的方法时,是给该对象发送消息,这里选择器起很大的作用,对象在得到消息后,寻找选择器方法表,得到函数地址,然后进行调用,这样,这里的id不需要任何类型转换,即可对对象进行消息的发送(即方法的调用),XCode对其也进行了智能提示,你将任何对象传递给id声明的变量,该变量就可智能提示所对应的类的所有方法,是不是很优美的面向对象的实现方式?
你试想,我们用id作为参数,动态创建类(可通过反射实现,后续章节中会介绍),然后通过配置文件动态执行方法,是不是可以很智能的实现对象化呢?
2) SEL
SEL类型就是为方法ID而产生的,所以它是用来表示方法ID的变量类型。
好,到此,我们可以理解选择器的两个特性: a. 可以通过名字找到方法,即可以通过名字调用方法,这是C/C++、C#和Java所没有的特性。b. 可以动态的改变方法的名字。
最后,还有个objc_msgSend, 下面引用自苹果官方文档对于消息机制的说明:
objc_msgSend函数