3.3 方法
3.3.1 方法的定义
在iOS开发中,通过将一则消息(message)发送给一个对象(称为消息的接收者),可以调用该对象的一个方法,消息机制是Objective-C语言的一个重要特点。在Objective-C中,有两种类型的方法,分别是实例方法与类方法。
1.有关方法的基本概念
在Objective-C语言中,调用某个对象中定义的方法是通过向对象发送消息的方式进行的,消息的名称对应类中定义的方法名称,这种机制是Objective-C语言的区别其他编程语言的一个特性,当需要深入研究和学习Objective-C语言时,理解其消息机制是非常重要的。当然,对于初学者来说,如何去调用类中定义的方法是需要优先掌握的内容。
在Objective-C中,调用一个对象的方法采用如下形式进行。其中,会涉及一些需要大家掌握的概念。
- 消息message:在iOS开发中,调用一个方法相当于传递一个消息,这里的消息指的是方法名(选择器Selector)和参数。消息传递(Message Passing)是Objective-C最大的特色,对象不是简单的调用方法,而是相互传递消息,这与C++有很大差异。
- 接收者receiver:通常为一个对象,消息告诉接收者需要去做什么事情。当消息发送的时候,系统从接收者的方法列表中选择最合适的方法并调用。
- 方法method:一般来说,方法都包括方法声明和方法实现两部分,相关代码分别编写在.h和.m文件中。通俗来说,方法就是需要对象去完成某个工作,以实现某种功能,可以简单理解为函数(实际上和函数也有差别)。
- 发送消息:当需要调用一个方法时,通过给实现该方法的对象发送一条消息来实现,简单来说,就是通知对象去调用其定义的某个方法或者其父类的某个方法。在发送的消息中,包含方法名称以及参数。
- 选择器selector:因为方法名在消息中负责在对象的方法列表中选择一个方法执行,因此方法名在消息中通常称为选择器。
2.方法的定义
方法声明包含了以下几个部分:方法类型标示符、返回类型、方法名称、参数类型和参数名称,如下所示。
其中:
- 方法类型标示符(-) 即这是一个实例方法。
- 返回类型(void) 即没有返回值。
- 方法名称(insertString:atIndex:) 一个方法的实际名称是所有签名关键词的串联,包括冒号字符。
- 参数类型 该方法中包括了两个参数,两个参数的类型为NSString和NSUInteger。
- 参数名称 该方法中包含了两个参数,两个参数的名称分别为aString和loc。
注意:在定义方法时,方法名称以及参数名称需要使用驼峰法来定义。
3.方法的类型
在iOS开发中,方法一共有两种类型,分别为实例方法和类方法。
- 实例方法:消息的接收者必须为一个已经实例化的对象,实例方法在定义时以“-”开头。例如:
- 类方法:有时也称为工厂方法,类方法通常用于创建类的新实例。消息的接收者为一个类对象(感观上即一个类的类名),类方法在定义时以“+”开头,类方法是一般情况下是有返回值的,返回类型通常为instancetype(即返回一个本类的对象)。
示例:
3.3.2 方法的调用
在Objective-C中,调用一个方法相当于传递一个消息,这里的消息指的是方法名和参数。所有消息的分派都是动态的,所谓动态指的是所有消息处理直到执行时(runtime)才会动态决定,而不是在编译时就绑定,这也体现了Objective-C对象的多态行为(多态性是指不同类型的对象响应同一消息的能力)。
1.方法调用的方式
在Objective-C中,调用一个方法相当于传递一个消息,消息中包含方法名(也称为选择器)和参数。通常调用方法存在以下几种方式。
- 普通调用:使用方括号将消息本身与参数放到括号内,同时将接收消息的对象放在最前面,如下所示。
运行结果如图3-6所示。
图3-6 运行结果
- 嵌套调用:有时为了避免声明大量的局部变量来存储临时结果,Objective-C也支持嵌套消息表达式。上面的案例中,可以不声明str2,从而对代码做如下改写:
- 调用父类的方法:子类可以直接调用父类的方法。如下所示:MYClass继承自NSObject,因此MYClass的对象myClass可以直接调用NSObject的copy方法。
2.点语法
Objective-C中还提供专门用于调用存取方法(setter/getter)的点语法。开发者可以调用getter/setter方法来获取/设置对象属性的值,同样的,可以使用点语法来更加简便地获取/设置对象属性的值。
下面的示例代码中,同时使用点语法对myClass对象的name属性赋值,然后又使用点语法来获取对应的值。
- 创建一个MYClass类,并且添加一个name属性。
- 使用点语法对name属性进行赋值以及取值操作。
运行结果如图3-7所示。
图3-7 运行结果
3.消息处理机制
为了深入理解消息、方法、接收者这些概念,必须了解消息处理的机制。在Objective-C中,消息是直到运行时才和方法进行绑定关联的。
消息机制的关键在于编译器为类和对象生成的结构。其中类的结构中包含两个基本元素:第一,指向父类的指针;第二,类的方法列表。而对象被创建时,对象的第一个实例变量是一个指向该对象的“类结构”的指针,即isa指针。通过该指针,就可以访问到该类及其父类的方法列表,如图3-8所示。
当向某个对象发送消息时:
- 首先根据isa指针,找到该对象对应的类结构的方法列表,继而即可找到具体的方法实现;当在本类的方法列表中找不到对应的方法时,会根据类结构中父类的指针去查找父类的方法列表,直至NSObject根类。
- 将对象以及参数传递给找到的方法实现。
- 执行方法中的代码,获取方法的返回值。
图3-8 消息机制
3.3.3 方法的重写
在Objective-C中,子类不仅可以继承父类的属性,同时还可以直接继承父类中的方法,而不需要重新编写相同的方法,但有时候在子类中并不想原封不动地继承父类中的方法,而是希望在子类中实现一些特定的功能,这时可以对父类的方法进行方法重写或方法覆盖。
1.方法重写的规则
一般来说,如果希望在子类中调用父类的某个方法,实现一些特定的功能时,可以考虑对父类的方法进行重写(当然也可以考虑新增一个方法,但这样做会使程序的可读性变差)。当子类需要重写父类的方法时,必须保证重写的两个方法返回值、方法名、参数列表完全一致。
方法的重写在iOS开发中十分常见,例如,当新增一个自定义控制器类时,系统会自动添加一些有关控制器的方法,如viewDidLoad方法,以便对方法进行重写。
2.示例代码
在下方的示例代码中,创建了一个父类以及一个子类,在子类中,对父类的方法进行了重写。
- 新增一个ClassA类,在ClassA.h文件中,添加webSite属性以及printWebSite方法。
- 在ClassA.m文件中,实现printWebSite方法的功能,即打印webSite属性的值。
- 新建一个ClassB,继承自ClassA。在ClassB.h文件中,同样添加一个printWebSite方法。
- ClassB.m文件中,重写printWebSite方法,改变打印的内容。
- 在main()中分别调用父类和子类的printWebSite方法如下所示。
运行结果如图3-9所示。
图3-9 运行结果
3.子类方法调用父类方法
在实际开发过程中,子类中经常会先调用一下父类的方法,然后再进行一些定制操作,例如在控制器类的viewDidLoad方法中,都需要首先执行[super viewDidLoad],然后在子类的viewDidLoad方法中进行一些额外操作。
接着上面的案例,对子类ClassB的printWebSite方法进行一些改进,使其首先调用一下父类的printWebSite方法,代码如下:
运行结果如图3-10所示。可以看到,当执行[classB printWebSite]时,会先调用ClassA的printWebSite方法,因此会打印出两条日志记录。
图3-10 运行结果