13.1 实例方法
实例方法与实例属性类似,都隶属于枚举、结构体或类的个体,即实例。通过实例化这些类型,创建实例,使用实例调用的方法。
我们上一章介绍了一个Account(银行账户)结构体,下面我们重新定义它为类,代码如下:
class Account {var amount : Double = 10_000.00 // 账户金额var owner : String = "Tony" // 账户名//计算利息func interestWithRate(rate : Double) -> Double { ①return rate * amount}}var myAccount = Account() ②//调用实例方法println(myAccount.interestWithRate(0.88)) ③
上述代码第①行定义了方法interestWithRate用来计算利息,从形式上看,方法与函数非常相似。第②行代码是实例化Account,myAccount是实例。第③行代码是调用方法,方法的调用前面要有主体,而函数不需要,例如myAccount.interestWithRate(0.88)是通过myAccount实例调用interestWithRate方法,调用操作符是“.”,与属性调用一样。
13.1.1 使用规范的命名
在Swift中,方法和函数的主要区别有以下3个。
方法的调用前面要有主体,而函数不需要。
方法是在枚举、结构体或类内部定义的。
方法命名规范与函数不同。
这一节我们主要讨论方法的命名规范问题。在很多人看来,这或许并不是一个技术问题,而是为了增强代码可读性所需要做的工作,因此很多人并不重视命名规范。然而在Swift中,方法命名规范却不仅仅是为了增强代码的可读性,更多的是出于与Objective-C混合编程的需要。Swift要求使用规范的命名是有历史原因的,目前苹果为iOS和Mac OS X应用开发提供的开发语言是Objective-C和Swift,同一个API两种语言共存。图13-1所示是iOS中的表视图数据源协议UITableViewDataSource。从图中可见,有两种语言(Swift和Objective-C)的API。

图 13-1 API比较
我们比较同一个方法的两个不同语言的描述:
- (UITableViewCell *)tableView:(UITableView *)tableViewcellForRowAtIndexPath:(NSIndexPath *)indexPath {} //Objective-Cfunc tableView(tableView: UITableView,cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {} //Swift
从这两种方法的命名和参数来看,它们非常相似,按照这样的规范命名后,很多Objective-C语言程序员能够很快地转到Swift语言上来。在我看来这就是苹果的良苦用心!
下面我们就详细介绍一下这些规范。首先Swift中的方法和Objective-C中的方法应该是极其相似的。Objective-C中的方法命名遵循了SmallTalk1语法风格,它将一个方法名分成几个部分,称为多重参数。假设定义一个按照索引插入对象(或实例)到集合里的一个方法,图13-2是Objective-C中的方法定义,图13-3是Swift中的方法定义。
1一种面向对象的语言。

图 13-2 Objective-C方法定义

图 13-3 Swift方法定义
方法中有两个参数,第一个参数是anObject,Swift中的anyObject相当于Objective-C中的id类型。第二个参数是index,Swift中的Int相当于Objectire-C中的NSInteger类型。Swift中方法的名称通常用一个介词(比如with,for,by)指向方法的第一个参数,如图13-3中的insertWithObject命名,从第二个参数后,可以指定外部参数名(atIndex)。如果没有指定,如图13-4所示,会将本地参数名(index)作为外部参数名,在函数中需要在本地参数名前加“#”,而方法不需要。

图 13-4 Swift方法定义
外部参数名是在方法外访问时使用的。我们可以采用如下代码调用该方法:
实例.insertWithObject("元素", atIndex : 1) //对应图13-3定义方法的调用实例.insertWithObject("元素", index : 1) //对应图13-4定义方法的调用
其中“元素”是要插入的数据,atIndex 是外部参数名,index是默认的外部参数名。
示例代码如下:
class Employee {var no : Int = 0var name : String = ""var job : String?var salary : Double = 0var dept : Department?}class Department {var no : Int = 0var name : String = ""var employees : [Employee] = [Employee]() ①func insertWithObject(anObject : AnyObject , atIndex index : Int)->() { ②let emp = anObject as Employee ③employees.insert(emp, atIndex:index) ④}}var dept = Department() ⑤var emp1 = Employee()dept.insertWithObject(emp1, atIndex: 0)var emp2 = Employee()dept.insertWithObject(emp2, atIndex: 0)var emp3 = Employee()dept.insertWithObject(emp3, atIndex: 0) ⑥println(dept.employees.count) ⑦
Employee与Department之间是一对多的关系,代码第①行说明一个部门包含多个员工,所以employees是一个Employee的数组。第②行代码是定义insertWithObject方法。在方法体中第③行是将类型为AnyObject的参数anObject强制转换为Employee类型。第④行按照索引插入数据。
代码第⑤~⑥行是实例化Department,并初始化插入3个员工实例。最后我们在第⑦行打印employees属性的长度。
如果在方法中不指定外部参数名,而是以本地参数作为外部参数名,则修改示例代码如下:
......func insertWithObject(anObject : AnyObject , index : Int)->() {let emp = anObject as Employeeemployees.insert(emp, atIndex:index)}......dept.insertWithObject(emp1, index: 0)
提示 如果
insertWithObject是函数,index参数前面是要加“#”号的,代码如下:
- func insertWithObject(anObject : AnyObject , #index : Int)->() {……}
13.1.2 结构体和枚举方法变异
结构体和枚举中的方法默认情况下是不能修改属性的。我们将上一节的Department定义改成结构体,代码如下:
class Employee {var no : Int = 0var name : String = ""var job : String?var salary : Double = 0var dept : Department?}struct Department {var no : Int = 0var name : String = ""var employees : [Employee] = [Employee]()func insertWithObject(anObject : AnyObject , index : Int)->() {let emp = anObject as Employeeemployees.insert(emp, atIndex:index) ①}}var dept = Department()var emp1 = Employee()dept.insertWithObject(emp1, index: 0)var emp2 = Employee()dept.insertWithObject(emp2, index: 0)var emp3 = Employee()dept.insertWithObject(emp3, index: 0)println(dept.employees.count)
上述程序代码第①行会发生编译错误,错误信息如下:
Playground execution failed: error: <REPL>:22:8: error: immutable value of type 'Employee[]' only has mutating members named 'insert'employees.insert(emp, atIndex:index)^ ~~~~~~
错误提示employees属性不可以修改。如果要修改,就要将方法声明为变异的(mutating)。修改方法声明如下:
......mutating func insertWithObject(anObject : AnyObject , index : Int)->() {let emp = anObject as Employeeemployees.insert(emp, atIndex:index)}......
我们在枚举和结构体方法前面添加关键字mutating,将方法声明为变异方法,变异方法能够修改变量属性,但不能修改常量属性。
