創(chuàng)建自己的結(jié)構
Swift可讓您通過兩種方式設計自己的類型,其中最常見的稱為結(jié)構狈蚤,或簡稱為structs
困肩。可以給結(jié)構賦予它們自己的變量和常量以及它們自己的功能炫惩,然后根據(jù)需要創(chuàng)建和使用它們僻弹。
讓我們從一個簡單的示例開始:我們將創(chuàng)建一個Sport
將其名稱存儲為字符串的結(jié)構。結(jié)構內(nèi)部的變量稱為properties
他嚷,因此這是具有一個屬性的結(jié)構:
struct Sport {
var name: String
}
定義了類型蹋绽,因此現(xiàn)在我們可以創(chuàng)建和使用它的實例:
var tennis = Sport(name: "Tennis")
print(tennis.name)
我們制作了name
和tennis
變量,因此可以像常規(guī)變量一樣更改它們:
tennis.name = "Lawn tennis"
屬性可以像常規(guī)變量一樣具有默認值筋蓖,并且通承对牛可以依靠Swift的類型推斷。
計算屬性
我們只是創(chuàng)建了一個Sport
這樣的結(jié)構:
struct Sport {
var name: String
}
具有存儲一個String
的屬性name
粘咖。這些稱為存儲屬性蚣抗,因為Swift具有另一種稱為計算屬性的屬性-一種運行代碼以計算其值的屬性。
我們將向該Sport
結(jié)構添加另一個存儲的屬性瓮下,然后是一個計算的屬性翰铡。看起來是這樣的:
struct Sport {
var name: String
var isOlympicSport: Bool
var olympicStatus: String {
if isOlympicSport {
return "\(name) is an Olympic sport"
} else {
return "\(name) is not an Olympic sport"
}
}
}
如您所見讽坏,它olympicStatus
看起來像一個普通的String
锭魔,但是它會根據(jù)其他屬性返回不同的值辛臊。
我們可以通過創(chuàng)建一個新實例Sport
來進行嘗試:
let chessBoxing = Sport(name: "Chessboxing", isOlympicSport: false)
print(chessBoxing.olympicStatus)
let gameBoxing = Sport(name: "gameBoxing", isOlympicSport: true)
print(gameBoxing.olympicStatus)
輸出內(nèi)容如下:
Chessboxing is not an Olympic sport
gameBoxing is an Olympic sport
屬性觀察器
通過屬性觀察器劈彪,可以在任何屬性更改之前或之后運行代碼邪乍。為了說明這一點,我們將編寫一個Progress
跟蹤任務和完成百分比的結(jié)構:
struct Progress {
var task: String
var amount: Int
}
現(xiàn)在硬霍,我們可以創(chuàng)建該結(jié)構的實例并隨時間調(diào)整其進度:
var progress = Progress(task: "Loading data", amount: 0)
progress.amount = 30
progress.amount = 80
progress.amount = 100
我們想讓 Swift每次amount
更改時都打印一條消息碱妆,我們可以使用didSet
屬性觀察器懈息。每次amount
更改時罕扎,它將運行一些代碼:
struct Progress {
var task: String
var amount: Int {
didSet {
print("\(task) is now \(amount)% complete")
}
}
}
您還可以willSet
用來在屬性更改之前采取措施,但這很少使用庆锦。
方法
結(jié)構內(nèi)部可以具有函數(shù)捅位,并且這些函數(shù)可以根據(jù)需要使用結(jié)構的屬性。結(jié)構內(nèi)部的函數(shù)稱為方法肥荔,但它們?nèi)允褂孟嗤?code>func關鍵字绿渣。
我們可以用一個City
結(jié)構來證明這一點。這將具有一個population
存儲城市人口的屬性燕耿,以及一個collectTaxes()
返回人口乘以1000的方法中符。由于該方法屬于City
,因此可以讀取當前城市的population
屬性誉帅。
這是代碼:
struct City {
var population: Int
func collectTaxes() -> Int {
return population * 1000
}
}
該方法屬于該結(jié)構淀散,因此我們在該結(jié)構的實例上調(diào)用它,如下所示:
let london = City(population: 9_000_000)
london.collectTaxes()
變異方法
如果一個結(jié)構具有可變屬性蚜锨,但是該結(jié)構的實例被創(chuàng)建為常量档插,則該屬性不能更改–該結(jié)構是恒定的,因此亚再,無論如何創(chuàng)建郭膛,其所有屬性也是恒定的。
問題在于氛悬,當您創(chuàng)建結(jié)構時则剃,Swift不知道是否將其與常量或變量一起使用,因此默認情況下采用安全方法:除非明確要求如捅,否則Swift不會允許您編寫更改屬性的方法棍现。
當您想在方法內(nèi)部更改屬性時,需要使用mutating
關鍵字對其進行標記镜遣,如下所示:
struct Person {
var name: String
mutating func makeAnonymous() {
name = "Anonymous"
}
}
因為它更改了屬性己肮,所以Swift僅允許在Person
作為變量的實例上調(diào)用該方法:
var person = Person(name: "Ed")
person.makeAnonymous()
字符串的屬性和方法
到目前為止,我們已經(jīng)使用了很多字符串悲关,事實證明它們是結(jié)構-它們具有自己的方法和屬性谎僻,可用于查詢和操作字符串。
首先寓辱,讓我們創(chuàng)建一個測試字符串:
let string = "Do or do not, there is no try."
您可以使用其count屬性讀取字符串中的字符數(shù):
print(string.count)
他們有一個hasPrefix()
方法艘绍,如果字符串以特定字母開頭,則返回true
:
print(string.hasPrefix("Do"))
您可以通過調(diào)用字符串的uppercased()
方法將其大寫:
print(string.uppercased())
您甚至可以讓Swift將字符串的字母排序成一個數(shù)組:
print(string.sorted())
字符串具有更多的屬性和方法-嘗試鍵入string.
以顯示Xcode的代碼完成選項讶舰。
數(shù)組的屬性和方法
數(shù)組也是結(jié)構,這意味著它們也具有自己的方法和屬性,可用于查詢和操作數(shù)組跳昼。
這是一個讓我們開始的簡單數(shù)組:
var toys = ["Woody"]
您可以使用其count屬性讀取數(shù)組中的項目數(shù):
print(toys.count)
如果要添加新項目般甲,請使用如下append()
方法:
toys.append("Buzz")
您可以使用其firstIndex()
方法在數(shù)組內(nèi)找到任何項,如下所示:
toys.firstIndex(of: "Buzz")
這將返回1鹅颊,因為數(shù)組從0開始計數(shù)敷存。
就像使用字符串一樣,您可以讓Swift將數(shù)組的項目按字母順序排序:
print(toys.sorted())
最后堪伍,如果要刪除項目锚烦,請使用如下remove()
方法:
toys.remove(at: 0)
數(shù)組具有更多的屬性和方法–嘗試鍵入toys.
以顯示Xcode的代碼完成選項。
初始化器
初始化器是特殊的方法帝雇,提供不同的方法來創(chuàng)建結(jié)構涮俄。默認情況下,所有結(jié)構都帶有一個名為成員級初始值設定項的值尸闸,這要求您在創(chuàng)建結(jié)構時為每個屬性提供一個值彻亲。
如果我們創(chuàng)建User
具有一個屬性的結(jié)構,則可以看到以下內(nèi)容:
struct User {
var username: String
}
創(chuàng)建這些結(jié)構之一時吮廉,必須提供用戶名:
var user = User(username: "twostraws")
我們可以提供自己的初始化程序來替換默認的初始化程序苞尝。例如,我們可能希望將所有新用戶創(chuàng)建為“匿名”并打印一條消息宦芦,如下所示:
struct User {
var username: String
init() {
username = "Anonymous"
print("Creating a new user!")
}
}
你不寫func
初始化之前宙址,但是你做的必要,以確保所有屬性都初始化結(jié)束前的值调卑。
現(xiàn)在我們的初始化程序不接受任何參數(shù)抡砂,我們需要創(chuàng)建如下結(jié)構:
var user = User()
user.username = "twostraws"
引用當前實例
在方法內(nèi)部,您會得到一個稱為的特殊常量self
令野,該常量指向當前正在使用的結(jié)構的任何實例舀患。當您創(chuàng)建與屬性具有相同參數(shù)名的初始值設定項時,此self
值特別有用气破。聊浅。
例如,如果您創(chuàng)建一個Person
帶有name
屬性的結(jié)構现使,然后嘗試編寫一個接受name
參數(shù)的初始化程序低匙,則self
可以幫助您區(qū)分屬性和參數(shù)– self.name
引用屬性,而name
引用參數(shù)碳锈。
這就是代碼中的內(nèi)容:
struct Person {
var name: String
init(name: String) {
print("\(name) was born!")
self.name = name
}
}
惰性屬性
作為性能優(yōu)化顽冶,Swift使您僅在需要時才創(chuàng)建一些屬性。舉個例子售碳,考慮一下這個FamilyTree
結(jié)構-它并沒有做很多强重,但是從理論上講绞呈,為某人創(chuàng)建家譜需要很長時間:
struct FamilyTree {
init() {
print("Creating family tree!")
}
}
我們可以將該FamilyTree
結(jié)構用作Person
結(jié)構內(nèi)部的屬性,如下所示:
struct Person {
var name: String
var familyTree = FamilyTree()
init(name: String) {
self.name = name
}
}
var ed = Person(name: "Ed")
但是间景,如果我們不總是需要特定人的家譜怎么辦佃声?如果將lazy
關鍵字添加到familyTree
屬性,則Swift僅在FamilyTree
首次訪問該結(jié)構時才會創(chuàng)建該結(jié)構:
lazy var familyTree = FamilyTree()
因此倘要,如果您想看到“正在創(chuàng)建家譜圾亏!”消息,則需要至少訪問一次該屬性:
ed.familyTree
靜態(tài)特性和方法
到目前為止封拧,我們創(chuàng)建的所有屬性和方法都屬于struct的各個實例志鹃,這意味著,如果我們有一個Student
struct泽西,我們可以創(chuàng)建多個學生實例曹铃,每個實例都具有各自的屬性和方法:
struct Student {
var name: String
init(name: String) {
self.name = name
}
}
let ed = Student(name: "Ed")
let taylor = Student(name: "Taylor")
您也可以通過將Swift聲明為static來要求Swift在該結(jié)構的所有實例之間共享特定的屬性和方法。
為了嘗試這一點尝苇,我們將向該Student
結(jié)構添加一個靜態(tài)屬性铛只,以存儲該班級中有多少學生。每次創(chuàng)建新學生時糠溜,我們都會向其中添加一個:
struct Student {
static var classSize = 0
var name: String
init(name: String) {
self.name = name
Student.classSize += 1
}
}
因為該classSize
結(jié)構屬于該結(jié)構本身而不是該結(jié)構的實例淳玩,所以我們需要使用讀取它Student.classSize
:
print(Student.classSize)
訪問控制
訪問控制允許您限制哪些代碼可以使用屬性和方法。這一點很重要非竿,例如您可能希望阻止人們直接讀取屬性蜕着。
我們可以創(chuàng)建一個Person
具有id
屬性以存儲其社會保險號的結(jié)構:
struct Person {
var id: String
init(id: String) {
self.id = id
}
}
let ed = Person(id: "12345")
創(chuàng)建該人后,我們可以將其id
設為私有红柱,因此您無法從結(jié)構外部讀取它-嘗試編寫ed.id
根本行不通承匣。
只需使用private
關鍵字,如下所示:
struct Person {
private var id: String
init(id: String) {
self.id = id
}
}
現(xiàn)在锤悄,只有內(nèi)部的方法Person
可以讀取id
屬性韧骗。例如:
struct Person {
private var id: String
init(id: String) {
self.id = id
}
func identify() -> String {
return "My social security number is \(id)"
}
}
另一個常見的選擇是public
,它允許所有其他代碼使用屬性或方法零聚。
總結(jié)
- 1.您可以使用結(jié)構創(chuàng)建自己的類型袍暴,這些結(jié)構可以具有自己的屬性和方法。
- 2.您可以使用存儲的屬性或使用計算的屬性即時計算值隶症。
- 3.如果要更改方法內(nèi)的屬性政模,則必須將其標記為
mutating
。 - 4.初始化程序是創(chuàng)建結(jié)構的特殊方法蚂会。默認情況下淋样,您將獲得一個成員初始化器,但是如果創(chuàng)建自己的初始化器胁住,則必須為所有屬性賦予一個值趁猴。
- 5.使用
self
常量來引用方法內(nèi)部結(jié)構的當前實例刊咳。 - 6.
lazy
關鍵字告訴Swift當?shù)谝淮问褂茫麄冎荒軇?chuàng)建屬性儡司。 - 7.您可以使用
static
關鍵字在結(jié)構的所有實例之間共享屬性和方法芦缰。 - 8.訪問控制使您可以限制可以使用屬性和方法的代碼。