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()
// 初始計數值是0
counter.increment()
// 計數值現在是1
counter.incrementBy(5)
// 計數值現在是6
counter.reset()
// 計數值現在是0
方法的局部參數名稱和外部參數名稱(Local and External Parameter Names for Methods)
函數參數可以同時有一個局部名稱(在函數體內部使用)和一個外部名稱(在調用函數時使用),詳情參見函數的外部參數名。方法參數也一樣(因爲方法就是函數,只是這個函數與某個類型相關聯了)。但是,方法和函數的局部名稱和外部名稱的默認行爲是不一樣的。
Swift 中的方法和 Objective-C 中的方法極其相似。像在 Objective-C 中一樣,Swift 中方法的名稱通常用一個介詞指向方法的第一個參數,比如:with
,for
,by
等等。前面的Counter
類的例子中incrementBy
方法就是這樣的。介詞的使用讓方法在被調用時能像一個句子一樣被解讀。和函數參數不同,對於方法的參數,Swift 使用不同的默認處理方式,這可以讓方法命名規範更容易寫。
具體來說,Swift 默認僅給方法的第一個參數名稱一個局部參數名稱;默認同時給第二個和後續的參數名稱局部參數名稱和外部參數名稱。這個約定與典型的命名和調用約定相適應,與你在寫 Objective-C 的方法時很相似。這個約定還讓表達式方法在調用時不需要再限定參數名稱。
看看下面這個Counter
的另一個版本(它定義了一個更復雜的incrementBy
方法):
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes: Int) {
count += amount * numberOfTimes
}
}
incrementBy
方法有兩個參數: amount
和numberOfTimes
。默認情況下,Swift 只把amount
當作一個局部名稱,但是把numberOfTimes
即看作局部名稱又看作外部名稱。下面調用這個方法:
let counter = Counter()
counter.incrementBy(5, numberOfTimes: 3)
// counter value is now 15
你不必爲第一個參數值再定義一個外部變量名:因爲從函數名incrementBy
已經能很清楚地看出它的作用。但是第二個參數,就要被一個外部參數名稱所限定,以便在方法被調用時明確它的作用。
這種默認的行爲能夠有效的處理方法(method),類似於在參數numberOfTimes
前寫一個井號(#
):
func incrementBy(amount: Int, #numberOfTimes: Int) {
count += amount * numberOfTimes
}
這種默認行爲使上面代碼意味着:在 Swift 中定義方法使用了與 Objective-C 同樣的語法風格,並且方法將以自然表達式的方式被調用。
修改方法的外部參數名稱(Modifying External Parameter Name Behavior for Methods)
有時爲方法的第一個參數提供一個外部參數名稱是非常有用的,儘管這不是默認的行爲。你可以自己添加一個顯式的外部名稱或者用一個井號(#
)作爲第一個參數的前綴來把這個局部名稱當作外部名稱使用。
相反,如果你不想爲方法的第二個及後續的參數提供一個外部名稱,可以通過使用下劃線(_
)作爲該參數的顯式外部名稱,這樣做將覆蓋默認行爲。
self
屬性(The self Property)
類型的每一個實例都有一個隱含屬性叫做self
,self
完全等同於該實例本身。你可以在一個實例的實例方法中使用這個隱含的self
屬性來引用當前實例。
上面例子中的increment
方法還可以這樣寫:
func increment() {
self.count++
}
實際上,你不必在你的代碼裏面經常寫self
。不論何時,只要在一個方法中使用一個已知的屬性或者方法名稱,如果你沒有明確的寫self
,Swift 假定你是指當前實例的屬性或者方法。這種假定在上面的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")
}
// 輸出 "This point is to the right of the line where x == 1.0"(這個點在x等於1.0這條線的右邊)
如果不使用self
前綴,Swift 就認爲兩次使用的x
都指的是名稱爲x
的函數參數。
在實例方法中修改值類型(Modifying Value Types from Within Instance Methods)
結構體和枚舉是值類型。一般情況下,值類型的屬性不能在它的實例方法中被修改。
但是,如果你確實需要在某個具體的方法中修改結構體或者枚舉的屬性,你可以選擇變異(mutating)
這個方法,然後方法就可以從方法內部改變它的屬性;並且它做的任何改變在方法結束時還會保留在原始結構中。方法還可以給它隱含的self
屬性賦值一個全新的實例,這個新實例在方法結束後將替換原來的實例。
要使用變異
方法, 將關鍵字mutating
放到方法的func
關鍵字之前就可以了:
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
println("The point is now at (\(somePoint.x), \(somePoint.y))")
// 輸出 "The point is now at (3.0, 4.0)"
上面的Point
結構體定義了一個變異方法(mutating method)moveByX
,moveByX
用來移動點。moveByX
方法在被調用時修改了這個點,而不是返回一個新的點。方法定義時加上mutating
關鍵字,這才讓方法可以修改值類型的屬性。
注意:不能在結構體類型常量上調用變異方法,因爲常量的屬性不能被改變,即使想改變的是常量的變量屬性也不行,詳情參見存儲屬性和實例變量
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveByX(2.0, y: 3.0)
// this will report an error
在變異方法中給self賦值(Assigning to self Within a Mutating Method)
變異方法能夠賦給隱含屬性self
一個全新的實例。上面Point
的例子可以用下面的方式改寫:
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
新版的變異方法moveByX
創建了一個新的結構(它的 x 和 y 的值都被設定爲目標值)。調用這個版本的方法和調用上個版本的最終結果是一樣的。
枚舉的變異方法可以把self
設置爲相同的枚舉類型中不同的成員:
enum TriStateSwitch {
case Off, Low, High
mutating func next() {
switch self {
case Off:
self = Low
case Low:
self = High
case High:
self = Off
}
}
}
var ovenLight = TriStateSwitch.Low
ovenLight.next()
// ovenLight 現在等於 .High
ovenLight.next()
// ovenLight 現在等於 .Off
上面的例子中定義了一個三態開關的枚舉。每次調用next
方法時,開關在不同的電源狀態(Off
,Low
,High
)之前循環切換。