从 C++到 Objective-C(5):类和对象(续二)

从 C++到 Objective-C(5):类和对象(续二)

成员函数的指针:选择器

在 Objective-C 中,方法具有包含了括号和标签的特殊语法。普通的函数不能使用这种语法。在 Objective-C 和 C 语言中,函数指针具有相同的概念,但是对于成员函数指针则有所不同。

在 C++ 中,尽管语法很怪异,但确实兼容 C 语言的:成员函数指针也是基于类型的。

C++

class Foo

{

public:

    int f(float x) {...}

};

 

Foo bar

int (Foo::*p_f)(float) = &Foo::f; // Foo::f 函数指针

(bar.*p_f)(1.2345); // 等价于 bar.f(1.2345);

在 Objective-C 中,引入了一个新的类型:指向成员函数的指针被称为选择器 selector。它的类型是 SEL,值通过 @selector 获得。@selector 接受方法名(包括 label)。使用类 NSInvocation 则可以通过选择器调用方法。大多时候,工具方法族 performSelector: (继承自 NSObject)更方便,约束也更大一些。其中最简单的三个是:

-(id) performSelector:(SEL)aSelector;

-(id) performSelector:(SEL)aSelector withObject:(id)anObjectAsParameter;

-(id) performSelector:(SEL)aSelector withObject:(id)anObjectAsParameter

                                     withObject:(id)anotherObjectAsParameter;

这些方法的返回值同被调用的函数的返回值是一样的。对于那些参数不是对象的方法,应该使用该类型的包装类,如 NSNumber 等。NSInvocation 也有类似的功能,并且更为强大。

按照前面的说法,我们没有任何办法阻止在一个对象上面调用方法,即便该对象并没有实现这个方法。事实上,当消息被接收到之后,方法会被立即触发。但是,如果对象并不知道这个方法,一个可被捕获的异常将被抛除,应用程序并不会被终止。我们可以使用 respondsToSelector: 方法来检查对象是否可被触发方法。

最后,@selector 的值是在编译器决定的,因此它并不会减慢程序的运行效率。

Objective-C

@interface Slave : NSObject {}

 

-(void) readDocumentation:(Document*)document;

@end

 

// 假设 array[] 是包含 10 个 Slave 对象的数组,

// document 是一个 Document 指针

// 正常的方法调用是

for(i=0 ; i<10 ; ++i)

    [array[i] readDocumentation:document];

 

// 下面使用 performSelector: 示例:

for(i=0 ; i<10 ; ++i)

    [array[i] performSelector:@selector(readDocumentation:)

                   withObject:document];

 

// 选择器的类型是 SEL

// 下面代码并不比前面的高效,因为 @selector() 是在编译器计算的

SEL methodSelector = @selector(readDocumentation:);

for(i=0 ; i<10 ; ++i)

    [slaves[i] performSelector:methodSelectorwithObject:document];

 

// 对于一个对象“foo”,它的类型是未知的(id)

// 这种测试并不是强制的,但是可以避免没有 readDocumentation: 方法时出现异常

if ([foo respondsToSelector:@selector(readDocumentation:)])

    [foo performSelector:@selector(readDocumentation:) withObject:document];

因此,选择器可被用作函数参数。通用算法,例如排序,就可以使用这种技术实现。

严格说来,选择器并不是一个函数指针。它的底层实现是一个 C 字符串,在运行时被注册为方法的标识符。当类被加载之后,它的方法会被自动注册到一个表中,所以 @selector 可以很好的工作。根据这种实现,我们就可以使用 == 来判断内存地址是否相同,从而得出选择器是否相同,而无需使用字符串函数。

方法的真实地址,也就是看做 C 字符串的地址,其实可以看作是 IMP 类型(我们以后会有更详细的说明)。这种类型很少使用,除了在做优化的时候。例如虚调用实际使用选择器处理,而不是 IMP。等价于 C++ 函数指针的 Objective-C 的概念是选择器,也不是 IMP。

最后,你应该记得我们曾经说过 Objective-C 里面的 self 指针,类似于 C++ 的 this 指针,是作为每一个方法的隐藏参数传递的。其实这里还有第二个隐藏参数,就是 _cmd。_cmd 指的是当前方法。

@implementation Foo

 

-(void) f:(id)parameter // 等价于 C 函数 void f(id self, SEL _cmd,id parameter)

{

    id currentObject = self;

    SEL currentMethod = _cmd;

    [currentObjectperformSelector:currentMethod

                        withObject:parameter]; // 递归调用

    [self performSelector:_cmd withObject:parameter]; // 也是递归调用

}

@end

参数的默认值

Objective-C 不允许参数带有默认值。所以,如果某些参数是可选的,那么就应当创建多个方法的副本。在构造函数中,这一现象成为指定构造函数(designated initializer)。

可变参数

Objective-C 允许可变参数,语法同 C 语言一样,使用 … 作为最后一个参数。这实际很少用到,即是 Cocoa 里面很多方法都这么使用。

匿名参数

C++ 允许匿名参数,它可以将不使用的参数类型作为一种占位符。Objective-C 不允许匿名参数。

原型修饰符(const,static,virtual,”= 0″,friend,throw)

在 C++ 中,还有一些可以作为函数原型的修饰符,但在 Objective-C 中,这都是不允许的。以下是这个的列表:

·        const:方法不能使用 const 修饰。既然没有了 const,也就不存在 mutable 了;

·        static:用于区别实例方法和类方法的是原型前面的 – 和 +;

·        virtual:Objective-C 中所有方法都是 virtual 的,因此没有必要使用这个修饰符。纯虚方法则是声明为一个典型的协议 protocol;

·        friend:Objective-C 里面没有 friend 这个概念;

·        throw:在 C++ 中,可以指定函数会抛除哪些异常,但是 Objective-C 不能这么做。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值