引言
繼續(xù)學習Swift文檔,從上一章節(jié):屬性,我們學習了Swift屬性相關(guān)的內(nèi)容或链,如存儲屬性和計算屬性set和get、懶加載档押、屬性監(jiān)聽willSet和didSet澳盐、屬性包裝@propertyWrapper、全局和局部變量在懶加載上的區(qū)別令宿、類屬性等這些內(nèi)容〉鸢遥現(xiàn)在,我們學習Swift的方法相關(guān)的內(nèi)容粒没。由于篇幅較長旬蟋,這里分篇來記錄,接下來革娄,F(xiàn)ighting倾贰!
這一章節(jié)的內(nèi)容比較簡單冕碟,熟悉的朋友可以直接跳到下一章節(jié):下標
方法
方法是與特定類型關(guān)聯(lián)的函數(shù)。 類匆浙,結(jié)構(gòu)體和枚舉都可以定義實例方法安寺,這些實例方法封裝了用于處理給定類型的實例的特定任務(wù)和功能。 類首尼,結(jié)構(gòu)體和枚舉也可以定義與類型本身關(guān)聯(lián)的類型方法挑庶。 類型方法類似于Objective-C中的類方法。
結(jié)構(gòu)體和枚舉可以在Swift中定義方法這一事實與C和Objective-C的主要不同之處软能。 在Objective-C中迎捺,類是唯一可以定義方法的類型。 在Swift中查排,您可以選擇是定義類凳枝,結(jié)構(gòu)體還是枚舉,并且仍然可以靈活地在創(chuàng)建的類型上定義方法跋核。
1 實例方法
實例方法是屬于特定類岖瑰,結(jié)構(gòu)體或枚舉的實例的函數(shù)。 它們通過提供訪問和修改實例屬性的方式砂代,或通過提供與實例用途相關(guān)的功能來支持這些實例的功能蹋订。 實例方法具有與函數(shù)完全相同的語法,如函數(shù)中所述刻伊。
您可以在實例方法的所屬類型的大括號內(nèi)編寫一個實例方法露戒。 實例方法可以隱式訪問該類型的所有其他實例方法和屬性。 實例方法只能在其所屬類型的特定實例上調(diào)用捶箱。 沒有現(xiàn)有實例玫锋,就不能單獨地調(diào)用它。
這是定義一個簡單的Counter類的示例讼呢,該類可用于計算操作發(fā)生的次數(shù):
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
Counter類定義了三個實例方法:
- increment()方法次數(shù)加1撩鹿。
- increment(by: Int)方法增加指定的次數(shù)。
- reset()方法將次數(shù)重置為0悦屏。
Counter類還定義了一個count屬性节沦,來跟蹤記錄當前的次數(shù)。
可以和調(diào)用屬性的方式一樣础爬,用點語法調(diào)用實例方法:
let counter = Counter()
// the initial counter value is 0
counter.increment()
// the counter's value is now 1
counter.increment(by: 5)
// the counter's value is now 6
counter.reset()
// the counter's value is now 0
函數(shù)參數(shù)可以具有名稱(供在函數(shù)體內(nèi)使用)和參數(shù)標簽(供在調(diào)用函數(shù)時使用)甫贯,如Function Argument Labels and Parameter Names中所述。 方法參數(shù)也是如此看蚜,因為方法只是與類型關(guān)聯(lián)的函數(shù)叫搁。
1.1 self屬性
一個類型的每個實例都有一個稱為self的隱式屬性,它與該實例本身完全等效。 您可以使用self屬性在其自己的實例方法中引用當前實例渴逻。
上例中的increment()方法可以這樣寫:
func increment() {
self.count += 1
}
實際上疾党,您不需要經(jīng)常在代碼中編寫自我。 如果您未明確編寫self惨奕,則Swift會假設(shè)您在方法中使用已知的屬性或方法名稱時雪位,都在引用當前實例的屬性或方法。 在Counter的三個實例方法中使用count(而不是self.count)可以證明這一假設(shè)梨撞。
·當實例方法的參數(shù)名稱與該實例的屬性名稱相同時雹洗,將發(fā)生此規(guī)則的主要例外。 在這種情況下卧波,參數(shù)名稱優(yōu)先时肿,因此有必要以更限定的方式引用該屬性。 您可以使用self屬性來區(qū)分參數(shù)名稱和屬性名稱港粱。
在這里螃成,self消除了方法參數(shù)x和實例屬性之間的歧義,該實例參數(shù)也稱為x:
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
print("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會假設(shè)x的兩種用法都引用了稱為x的方法參數(shù)锈颗。
1.2 從實例方法中修改值類型
結(jié)構(gòu)體和枚舉是值類型顷霹。 默認情況下咪惠,不能從其實例方法中修改值類型的屬性。
但是淋淀,如果需要在特定方法中修改結(jié)構(gòu)體或枚舉的屬性遥昧,則可以選擇對該方法進行行為更改。 然后朵纷,該方法可以從方法內(nèi)部更改(即更改)其屬性炭臭,并在方法結(jié)束時將其所做的任何更改寫回到原始結(jié)構(gòu)。 該方法還可以為其隱式的self屬性分配一個全新的實例袍辞,并且該新實例將在方法結(jié)束時替換現(xiàn)有實例鞋仍。
您可以通過將mutating關(guān)鍵字放在該方法的func關(guān)鍵字之前來選擇這種行為:
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// Prints "The point is now at (3.0, 4.0)"
上面的Point結(jié)構(gòu)體定義了一個可變的moveBy(x:y :)方法,該方法將Point實例移動一定量搅吁。 實際上威创,此方法不是返回新的點,而是修改了在其上被調(diào)用的點谎懦。 將mutating關(guān)鍵字添加到其定義中肚豺,以使其能夠修改其屬性。
請注意界拦,您不能在結(jié)構(gòu)體類型的常量上調(diào)用可變的(mutating)方法吸申,因為它的屬性即使是可變屬性也無法更改,如“常量結(jié)體構(gòu)實例的存儲屬性”中所述:
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// this will report an error
1.3 在mutating方法中分配self
可變方法可以為隱式的self屬性分配一個全新的實例。 上面顯示的Point示例可以用以下方式編寫:
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
此版本的mutableBy(x:y :)方法將創(chuàng)建一個新結(jié)構(gòu)體截碴,其x和y值設(shè)置為目標位置梳侨。 調(diào)用此方法的替代版本的最終結(jié)果將與調(diào)用早期版本的結(jié)果完全相同。
枚舉的可變方法可以將隱式self參數(shù)設(shè)置為與相同枚舉的不同的case:
enum s {
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 is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off
本示例定義了三態(tài)開關(guān)的枚舉隐岛。 每次調(diào)用next()方法時猫妙,開關(guān)都會在三種不同的電源狀態(tài)(關(guān),低和高)之間循環(huán)聚凹。
2 類方法
如上所述割坠,實例方法是您在特定類型的實例上調(diào)用的方法。 您還可以定義在類型本身上調(diào)用的方法妒牙。 這些方法稱為類方法彼哼。 您可以通過在方法的func關(guān)鍵字之前寫入static關(guān)鍵字來指示類方法。 類可以改用class關(guān)鍵字湘今,以允許子類覆蓋父類對該方法的實現(xiàn)敢朱。
注意
在Objective-C中,您只能為Objective-C類定義類型級別的方法摩瞎。 在Swift中拴签,您可以為所有類,結(jié)構(gòu)體和枚舉定義類型級別的方法旗们。 每個類方法都明確地限定于它支持的類型蚓哩。
類方法使用點語法來調(diào)用,就像實例方法一樣上渴。 但是岸梨,您在類型上而不是在該類型的實例上調(diào)用類方法。 在名為SomeClass的類上調(diào)用類方法的方法如下:
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()
在類方法的主體內(nèi)稠氮,隱式self屬性是指類型本身曹阔,而不是該類型的實例。這意味著您可以使用self來區(qū)分類型屬性和類方法參數(shù)隔披,就像處理實例屬性和實例方法參數(shù)一樣赃份。
一般而言,您在類方法主體內(nèi)使用的任何不符合的方法和屬性名稱都將引用其他類型級別的方法和屬性奢米。一個類方法可以使用另一個方法的名稱來調(diào)用另一個類方法抓韩,而無需在其前面加上類型名稱。同樣恃慧,結(jié)構(gòu)體和枚舉上的類方法可以使用類型屬性的名稱訪問類型屬性园蝠,而無需使用類型名稱前綴。
下面的示例定義了一個稱為LevelTracker的結(jié)構(gòu)體痢士,該結(jié)構(gòu)體可跟蹤玩家在游戲的不同關(guān)卡或階段中的進度彪薛。這是一款單人游戲茂装,但可以在單個設(shè)備上存儲多個玩家的信息。
首次玩游戲時善延,所有游戲級別(除了第一級)都被鎖定少态。每當玩家完成一個關(guān)卡時,設(shè)備上的所有玩家都將解鎖該關(guān)卡易遣。 LevelTracker結(jié)構(gòu)體使用類型屬性和方法來跟蹤游戲的哪些級別已解鎖彼妻。它還跟蹤單個玩家的當前級別。
struct LevelTracker {
static var highestUnlockedLevel = 1
var currentLevel = 1
static func unlock(_ level: Int) {
if level > highestUnlockedLevel { highestUnlockedLevel = level }
}
static func isUnlocked(_ level: Int) -> Bool {
return level <= highestUnlockedLevel
}
@discardableResult
mutating func advance(to level: Int) -> Bool {
if LevelTracker.isUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}
LevelTracker結(jié)構(gòu)體跟蹤所有玩家已解鎖的最高級別豆茫。 此值存儲在名為maximumUnlockedLevel的類型屬性中侨歉。
LevelTracker還定義了兩個類型函數(shù),以與highestUnlockedLevel屬性一起使用揩魂。 第一個是稱為unlock(_ :)的類型函數(shù)幽邓,每當解鎖新級別時,該函數(shù)都會更新highestUnlockedLevel的值火脉。 第二個是方便類型的函數(shù)牵舵,稱為isUnlocked(_ :),如果特定級別的數(shù)字已被解鎖倦挂,則返回true畸颅。 (請注意,這些類型方法可以訪問maximumUnlockedLevel類型屬性方援,而無需將其編寫為LevelTracker.highestUnlockedLevel没炒。)
除了其類型屬性和類方法之外,LevelTracker還可以跟蹤單個玩家在游戲中的進度肯骇。 它使用稱為currentLevel的實例屬性來跟蹤玩家當前正在玩的級別窥浪。
為了幫助管理currentLevel屬性祖很,LevelTracker定義了一個稱為advance(to :)的實例方法笛丙。 在更新currentLevel之前,此方法檢查所請求的新級別是否已被解鎖假颇。 advance(to :)方法返回一個布爾值胚鸯,以指示它是否實際上能夠設(shè)置currentLevel。 由于調(diào)用advance(to :)忽略返回值不一定是錯誤的代碼笨鸡,因此此函數(shù)標有@discardableResult屬性姜钳。 有關(guān)此屬性的更多信息,請參見Attributes形耗。
LevelTracker結(jié)構(gòu)體與Player類一起使用哥桥,如下所示,用于跟蹤和更新單個玩家的進度:
class Player {
var tracker = LevelTracker()
let playerName: String
func a(level: Int) {
LevelTracker.unlock(level + 1)
tracker.advance(to: level + 1)
}
init(name: String) {
playerName = name
}
}
Player類創(chuàng)建一個新的LevelTracker實例來跟蹤該玩家的進度激涤。 它還提供了一種稱為complete(level :)的方法拟糕,只要玩家完成特定的關(guān)卡,就會調(diào)用該方法。 此方法為所有玩家解鎖下一個級別送滞,并更新玩家的進度以將他們移至下一個級別侠草。 (因為已知該級別已被上一行的LevelTracker.unlock(_ :)調(diào)用解鎖,所以會忽略advance(to :)的布爾返回值犁嗅。)
您可以為新玩家創(chuàng)建Player類的實例边涕,并查看當玩家完成第一級時會發(fā)生什么:
var player = Player(name: "Argyrios")
player.complete(level: 1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// Prints "highest unlocked level is now 2"
如果您創(chuàng)建第二個玩家,嘗試將其移動到游戲中尚未被任何玩家解鎖的級別褂微,則設(shè)置該玩家當前級別的嘗試將失敼︱选:
player = Player(name: "Beto")
if player.tracker.advance(to: 6) {
print("player is now on level 6")
} else {
print("level 6 has not yet been unlocked")
}
// Prints "level 6 has not yet been unlocked"
總結(jié)
這一章節(jié)內(nèi)容比較簡單,主要是實例方法和類方法的定義和使用宠蚂;這里特別說一下swift里的類方法定義使用static關(guān)鍵詞霞赫,還可以使用class關(guān)鍵詞修飾方法,表示子類的方法可以覆蓋父類的方法肥矢。還有結(jié)構(gòu)體和枚舉里的方法端衰,默認情況下是不可以修改結(jié)構(gòu)體和枚舉里定義的屬性的,不過可以使用mutating關(guān)鍵詞修飾方法甘改,達到修改屬性的目的旅东。
最后,有收獲的朋友麻煩動動手指點個贊哦十艾,謝謝~
參考文檔:Swift - Methods