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 )tableView
cellForRowAtIndexPath:(NSIndexPath )indexPath {} //Objective-C
func 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 = 0
var name : String = ""
var job : String?
var salary : Double = 0
var dept : Department?
}
class Department {
var no : Int = 0
var 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 Employee
employees.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 = 0
var name : String = ""
var job : String?
var salary : Double = 0
var dept : Department?
}
struct Department {
var no : Int = 0
var name : String = ""
var employees : [Employee] = [Employee]()
func insertWithObject(anObject : AnyObject , index : Int)->() {
let emp = anObject as Employee
employees.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 Employee
employees.insert(emp, atIndex:index)
}
......
我们在枚举和结构体方法前面添加关键字mutating
,将方法声明为变异方法,变异方法能够修改变量属性,但不能修改常量属性。