在 Objective-C 中,方法具有包含了括号和标签的特殊语法。普通的函数不能使用这种语法。在Objective-C 和 C 语言中,函数指针具有相同的概念,但是对于成员函数指针则有所不同。
在 C++ 中,尽管语法很怪异,但确实兼容 C 语言的:成员函数指针也是基于类型的。
C++
在 Objective-C 中,引入了一个新的类型:指向成员函数的指针被称为选择器 selector。它的类型是SEL,值通过 @selector 获得。@selector 接受方法名(包括 label)。使用类 NSInvocation则可以通过选择器调用方法。大多时候,工具方法族 performSelector: (继承自NSObject)更方便,约束也更大一些。其中最简单的三个是:
这些方法的返回值同被调用的函数的返回值是一样的。对于那些参数不是对象的方法,应该使用该类型的包装类,如 NSNumber等。NSInvocation 也有类似的功能,并且更为强大。
按照前面的说法,我们没有任何办法阻止在一个对象上面调用方法,即便该对象并没有实现这个方法。事实上,当消息被接收到之后,方法会被立即触发。但是,如果对象并不知道这个方法,一个可被捕获的异常将被抛除,应用程序并不会被终止。我们可以使用respondsToSelector: 方法来检查对象是否可被触发方法。
最后,@selector 的值是在编译器决定的,因此它并不会减慢程序的运行效率。
Objective-C
因此,选择器可被用作函数参数。通用算法,例如排序,就可以使用这种技术实现。
严格说来,选择器并不是一个函数指针。它的底层实现是一个 C字符串,在运行时被注册为方法的标识符。当类被加载之后,它的方法会被自动注册到一个表中,所以 @selector可以很好的工作。根据这种实现,我们就可以使用 == 来判断内存地址是否相同,从而得出选择器是否相同,而无需使用字符串函数。
方法的真实地址,也就是看做 C 字符串的地址,其实可以看作是 IMP类型(我们以后会有更详细的说明)。这种类型很少使用,除了在做优化的时候。例如虚调用实际使用选择器处理,而不是 IMP。等价于 C++函数指针的 Objective-C 的概念是选择器,也不是 IMP。
最后,你应该记得我们曾经说过 Objective-C 里面的 self 指针,类似于 C++ 的 this指针,是作为每一个方法的隐藏参数传递的。其实这里还有第二个隐藏参数,就是 _cmd。_cmd 指的是当前方法。
参数的默认值
Objective-C不允许参数带有默认值。所以,如果某些参数是可选的,那么就应当创建多个方法的副本。在构造函数中,这一现象成为指定构造函数(designatedinitializer)。
可变参数
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 不能这么做。