13.1 实例方法

实例方法与实例属性类似,都隶属于枚举、结构体或类的个体,即实例。通过实例化这些类型,创建实例,使用实例调用的方法。

我们上一章介绍了一个Account(银行账户)结构体,下面我们重新定义它为类,代码如下:

  1. class Account {
  2. var amount : Double = 10_000.00 // 账户金额
  3. var owner : String = "Tony" // 账户名
  4. //计算利息
  5. func interestWithRate(rate : Double) -> Double {
  6. return rate * amount
  7. }
  8. }
  9. var myAccount = Account()
  10. //调用实例方法
  11. println(myAccount.interestWithRate(0.88))

上述代码第①行定义了方法interestWithRate用来计算利息,从形式上看,方法与函数非常相似。第②行代码是实例化AccountmyAccount是实例。第③行代码是调用方法,方法的调用前面要有主体,而函数不需要,例如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比较

我们比较同一个方法的两个不同语言的描述:

  1. - (UITableViewCell *)tableView:(UITableView )tableView
  2. cellForRowAtIndexPath:(NSIndexPath )indexPath {} //Objective-C
  3. func tableView(tableView: UITableView,
  4. 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中方法的名称通常用一个介词(比如withforby)指向方法的第一个参数,如图13-3中的insertWithObject命名,从第二个参数后,可以指定外部参数名(atIndex)。如果没有指定,如图13-4所示,会将本地参数名(index)作为外部参数名,在函数中需要在本地参数名前加“#”,而方法不需要。

{%}

图 13-4 Swift方法定义

外部参数名是在方法外访问时使用的。我们可以采用如下代码调用该方法:

  1. 实例.insertWithObject("元素", atIndex : 1) //对应图13-3定义方法的调用
  2. 实例.insertWithObject("元素", index : 1) //对应图13-4定义方法的调用

其中“元素”是要插入的数据,atIndex 是外部参数名,index是默认的外部参数名。

示例代码如下:

  1. class Employee {
  2. var no : Int = 0
  3. var name : String = ""
  4. var job : String?
  5. var salary : Double = 0
  6. var dept : Department?
  7. }
  8. class Department {
  9. var no : Int = 0
  10. var name : String = ""
  11. var employees : [Employee] = [Employee]()
  12. func insertWithObject(anObject : AnyObject , atIndex index : Int)->() {
  13. let emp = anObject as Employee
  14. employees.insert(emp, atIndex:index)
  15. }
  16. }
  17. var dept = Department()
  18. var emp1 = Employee()
  19. dept.insertWithObject(emp1, atIndex: 0)
  20. var emp2 = Employee()
  21. dept.insertWithObject(emp2, atIndex: 0)
  22. var emp3 = Employee()
  23. dept.insertWithObject(emp3, atIndex: 0)
  24. println(dept.employees.count)

EmployeeDepartment之间是一对多的关系,代码第①行说明一个部门包含多个员工,所以employees是一个Employee的数组。第②行代码是定义insertWithObject方法。在方法体中第③行是将类型为AnyObject的参数anObject强制转换为Employee类型。第④行按照索引插入数据。

代码第⑤~⑥行是实例化Department,并初始化插入3个员工实例。最后我们在第⑦行打印employees属性的长度。

如果在方法中不指定外部参数名,而是以本地参数作为外部参数名,则修改示例代码如下:

  1. ......
  2. func insertWithObject(anObject : AnyObject , index : Int)->() {
  3. let emp = anObject as Employee
  4. employees.insert(emp, atIndex:index)
  5. }
  6. ......
  7. dept.insertWithObject(emp1, index: 0)

提示 如果insertWithObject是函数,index参数前面是要加“#”号的,代码如下:

  1. func insertWithObject(anObject : AnyObject , #index : Int)->() {……}

13.1.2 结构体和枚举方法变异

结构体和枚举中的方法默认情况下是不能修改属性的。我们将上一节的Department定义改成结构体,代码如下:

  1. class Employee {
  2. var no : Int = 0
  3. var name : String = ""
  4. var job : String?
  5. var salary : Double = 0
  6. var dept : Department?
  7. }
  8. struct Department {
  9. var no : Int = 0
  10. var name : String = ""
  11. var employees : [Employee] = [Employee]()
  12. func insertWithObject(anObject : AnyObject , index : Int)->() {
  13. let emp = anObject as Employee
  14. employees.insert(emp, atIndex:index) ①
  15. }
  16. }
  17. var dept = Department()
  18. var emp1 = Employee()
  19. dept.insertWithObject(emp1, index: 0)
  20. var emp2 = Employee()
  21. dept.insertWithObject(emp2, index: 0)
  22. var emp3 = Employee()
  23. dept.insertWithObject(emp3, index: 0)
  24. println(dept.employees.count)

上述程序代码第①行会发生编译错误,错误信息如下:

  1. Playground execution failed: error: <REPL>:22:8: error: immutable value of type 'Employee[]' only has mutating members named 'insert'
  2. employees.insert(emp, atIndex:index)
  3. ^ ~~~~~~

错误提示employees属性不可以修改。如果要修改,就要将方法声明为变异的(mutating)。修改方法声明如下:

  1. ......
  2. mutating func insertWithObject(anObject : AnyObject , index : Int)->() {
  3. let emp = anObject as Employee
  4. employees.insert(emp, atIndex:index)
  5. }
  6. ......

我们在枚举和结构体方法前面添加关键字mutating,将方法声明为变异方法,变异方法能够修改变量属性,但不能修改常量属性。