Files
the-swift-programming-langu…/source/chapter2/11_Methods.md

6.8 KiB
Raw Blame History

方法(Methods)

方法是与某些特定类型相关联的功能/函数。类、结构体、枚举都可以定义实例方法;实例方法为指定类型的实例封装了特定的任务与功能。类、结构体、枚举也可以定义类(型)方法(type itself)类型方法与类型自身相关联。类型方法与Objective-C中的类方法(class methods)相似。

在Swift中,结构体和枚举能够定义方法事实上这是Swift与C/Objective-C的主要区别之一。在Objective-C中,类是唯一能定义方法的类型。在Swift中你能够选择是否定义一个类/结构体/枚举,并且你仍然享有在你创建的类型(类/结构体/枚举)上定义方法的灵活性。

实例方法(Instance Methods)

实例方法是某个特定类、结构体或者枚举类型的实例的方法。实例方法支撑实例的功能: 或者提供方法,以访问和修改实例属性;或者提供与实例的目的相关的功能。实例方法的语法与函数完全一致,参考函数说明

实例方法要写在它所属的类型的前后括号之间。实例方法能够访问他所属类型的所有的其他实例方法和属性。实例方法只能被它所属的类的特定实例调用。实例方法不能被孤立于现存的实例而被调用。

下面是定义一个很简单的类Counter的例子(Counter能被用来对一个动作发生的次数进行计数):

class Counter {
  var count = 0
  func increment() {
    count++
  }
  func incrementBy(amount: Int) {
    count += amount
  }
  func reset() {
    count = 0
  }
}

Counter类定理了三个实例方法:

  • increment让计数器按一递增;
  • incrementBy(amount: Int)让计数器按一个指定的整数值递增;
  • reset将计数器重置为0。

Counter这个类还声明了一个可变属性count,用它来保持对当前计数器值的追踪。

和调用属性一样,用点语法(dot syntax)调用实例方法:

 let counter = Counter()
 // the initial counter value is 0
 counter.increment()
 // the counter's value is now 1
 counter.incrementBy(5)
 // the counter's value is now 6
 counter.reset()
 // the counter's value is now 0

方法的局部和外部参数名称(Local and External Parameter Names for Methods)

函数参数有一个局部名称(在函数体内部使用)和一个外部名称(在调用函数时使用),参考External Parameter Names。对于方法参数也是这样,因为方法就是函数(只是这个函数与某个类型相关联了)。但是,方法和函数的局部名称和外部名称的默认行为是不一样的。

Swift中的方法和Objective-C中的方法极其相似。像在Objective-C中一样Swift中方法的名称通常用一个介词指向方法的第一个参数比如with,for,by等等。前面的Counter类的例子中incrementBy方法就是这样的。介词的使用让方法在被调用时能像一个句子一样被解读。Swift这种方法命名约定很容易落实,因为它是用不同的默认处理方法参数的方式,而不是用函数参数(来实现的)。

具体来说Swift默认仅给方法的第一个参数名称一个局部参数名称;但是默认同时给第二个和后续的参数名称局部参数名称和外部参数名称。 这个约定与典型的命名和调用约定相匹配这与你在写Objective-C的方法时很相似。这个约定还让expressive method调用不需要再检查/限定参数名。

看看下面这个Counter的替换版本(它定义了一个更复杂的incrementBy方法):

1| class Counter {
2|   var count: Int = 0
3|   func incrementBy(amount: Int, numberOfTimes: Int) {
4|     count += amount * numberOfTimes
5|   }
6| }

incrementBy方法有两个参数: amountnumberOfTimes。默认地Swift只把amount当作一个局部名称,但是把numberOfTimes即看作本地名称又看作外部名称。下面调用这个方法:

1| let counter = Counter()
2| counter.incrementBy(5, numberOfTimes: 3)
3| // counter value is now 15

你不必为第一个参数值再定义一个外部变量名:因为从函数名incrementBy已经能很清楚地看出它的目的/作用。但是第二个参数,就要被一个外部参数名称所限定,以便在方法被调用时让他目的/作用明确。

这种默认的行为能够有效的检查方法比如你在参数numberOfTimes前写了个井号( # )时:

1| func incrementBy(amount: Int, #numberOfTimes: Int) {
2|  count += amount * numberOfTimes
3| }

这种默认行为使上面代码意味着在Swift中定义方法使用了与Objective-C同样的语法风格并且方法将以自然表达式的方式被调用。

Modifying External Parameter Name Behavior for Methods

有时为方法的第一个参数提供一个外部参数名称是非常有用的,尽管这不是默认的行为。你可以自己添加一个明确的外部名称;你也可以用一个hash符号作为第一个参数的前缀然后用这个局部名字作为外部名字。

相反,如果你不想为方法的第二个及后续的参数提供一个外部名称,你可以通过使用下划线(_)作为该参数的显式外部名称来覆盖默认行为。

The self Property

类型的每一个实例都有一个隐含属性叫做self,它完全等同于这个实力变量本身。你可以在一个实例的实例方法中使用这个隐含的self属性来引用当前实例。

上面例子中的increment方法可以被写成这样:

func increment() {
  self.count++
}

实际上,你不必在你的代码里面经常写self。不论何时,在一个方法中使用一个已知的属性或者方法名称,如果你没有明确的写selfSwift假定你是指当前实例的属性或者方法。这种假定在上面的Counter中已经示范了:Counter中的三个实例方法中都使用的是count(而不是self.count)

这条规则的主要例外发生在当实例方法的某个参数名称与实例的某个属性名称相同时。 在这种情况下,参数名称享有优先权,并且在引用属性时必须使用一种更恰当(被限定更严格)的方式。 你可以使用隐藏的self属性来区分参数名称和属性名称。

下面的例子演示了self消除方法参数x和实例属性x之间的歧义:

struct Point {
  var x = 0.0, y = 0.0
  func isToTheRightOfX(x: Double) -> Bool {
    return self.x > x
  }
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOfX(1.0) {
  println("This point is to the right of the line where x == 1.0")
}
// prints "This point is to the right of the line where x == 1.0"

如果不使用self前缀Swift就认为两次使用的x都指的是名称为x的函数参数。

Modifying Value Types from Within Instance Methods