Methods 方法

要说 Swift 和 Objective-C 在最相似的地方,可能就是方法(method)了。从某种意义上讲,学习 Objective-C 中的方法可以帮助我们更好地理解 Swift 的语法。

我们先从简单的开始吧~来创建一个 printGreeting 方法,它没有参数传进来,也不返回任何值,写出来是这样的:

- (void)printGreeting {
    NSLog(@"Hello!");
}

我们把这个方法放到 Person.m 文件里面的 @implementation 和 @end 这两行之间,来看看代码的细节:

  • "-" 符号写在实例方法前面,有时候我们会写 "+" 符号,这表示类方法。
  • 返回值的类型写在方法名的前面,这里 void 代表的是「不返回任何值」。
  • 注意空格的位置,在 - 后面是有空格的,但是在 (void) 和方法名之间,是没有空格的。

如果我们想在 main.m 使用这个方法,光写上面的代码是不够的。现在我们来打开 main.m 文件,把下面这段代码粘在原来的那个函数那里。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *person = [Person new];
        [person printGreeting];
    }
return 0; }

这就在 main.m 文件里创造了一个 Person 对象,然后调用了 printGreeting 方法。但这里是无法编译的,你会收到这样一个错误信息:"Use of undeclared identifier ‘person’, did you mean 'pVersion'?" ,它提示你使用了一个没有定义的东西,而这样做是非法的。

在 Swift 里,你创建的任何文件都会自动在项目工程里生成命名空间,也就是说,你在 A 文件定义的类,可以在 B 文件里用。但是 Objective-C 里就不能这样做了,我们要 #import 头文件,才能使用对应的类。所以在 main.m 文件的顶端,我们要引入 Person 类的头文件:

#import "Person.h"

但这个时候,程序依然无法被编译。这个时候错误的信息变成了:"No visible @interface for ‘Person’ declares the selector 'printGreeting'"

它的意思是:「在 Person 类里没有找到 printGreeting 选择器」。

这个错误信息产生的原因是,我们的 printGreeting 方法,现在还是一个私有方法。就是说它写在了实现文件里,并没有在头文件里被暴露出来。

怎么搞定这个问题呢?我们要在 Person.h 文件里面,把 printGreeting 方法的接口亮出来,在 @interface 和 @end 这两行之间把这句话加上去:

- (void)printGreeting;

敲黑板啦! main.m 文件只引入头文件,也就是说,它是通过头文件里的接口调用这个类的属性和方法。

然而,写在实现文件里的方法,不管在头文件里面它的接口是否被暴露出来,都是可以在实现文件里面调用的。换句话说就是,只要这个方法不在头文件里出现,它就是完全私有的。

这里要小小地插一句题外话,就是当你接手一个用老语法写的 Objective-C 项目的时候,你会发现老版本的 Xcode 对于没有声明的类的处理方法和现在是不一样的。如果我们没有在 Person.h 文件里声明 printGreeting 方法,他在实现文件里面的调用也不是全局的,而是写完这个方法之后才能调用。也就是说,如果有一个 printEverything 方法,来调用 printGreeting 方法, printEverything 方法必须写在 printGreeting 之后,否则就无法调用。

所以说,如果你看到了比较 Objective-C 的代码,你会发现所有的方法都会在头文件里声明,哪怕这个方法并不是用在外部的。

再提一句,这个就比较重要了, Objective-C 实际上是没有私有函数这个概念的。哪怕我们不在头文件声明,依然有办法调用实现文件里的方法。举个例子,你可以在 Person.h 里删掉 printGreeting 的借口,然后把 main.m 里的代码换成下面的这段,看看能不能调用嘞?

Person *person = [Person new];
[person performSelector:@selector(printGreeting)];

然而,好像又报错了?这里用到的是 performSelector,也就是用了一个选择器(selector)。它和方法之间有一些微妙的区别,大多数情况下,这个无关紧要,但是,在某种情况下,就会引起报错,这种情况就是:这个方法写在一个类的实现文件里面,而选择器用了和这个方法相同的名称,比如上面的 printGreeting。

这个区别其实很不科学的,下面这段话你可能要读上好几遍,然后才会有一个大概的印象。

假设我们有另外一个类,这个类叫做 Dog,他同样有 printGreeting 方法,这个方法和我们在 Person 类里的 printGreeting 方法看起来一毛一样,没有参数也没有返回值。区别只是 Dog 类的头文件里,声明了 printGreeting 方法。尽管它们一个在 Dog 里面,一个在 Person 里面,是完全不同的两个方法,但是他们的选择器是一样的。现在你看到的错误信息是 "Undeclared selector 'printGreeting'",即「使用了没有声明的选择器」。这个因为你没有在 Person.h 里面声明该方法。但是如果你把我们想象出来的 Dog 类引入头文件里,这个报错就消失了。因为在使用选择器的时候,Xcode 不在意 printGreeting 在哪里被声明,只要声明就可以了,管他在哪里呢。

这也就是说,如果你想在某个对象上用选择器调用某个方法,只要这个方法被声明过,Xcode 就不会报错。这就有毒了,如果你选择的这个对象完全不支持这个方法,编译是不会有问题的,但是一运行软件就崩溃了。

所以如果你要确定,你选择的这个对象是支持调用某一方法的,在用选择器的时候,用 respondsToSelector 方法会比较好。

results matching ""

    No results matching ""