# Swift 結構體
## 聲明語法
```
struct 結構體名稱 {
}
```
> 結構體的定義使用 `struct` 關鍵字開始,由于結構體也是一種新的數據類型,所以首字母需要大寫。
## 聲明結構體
```
struct Location { // Location 為數據類型,首字母大寫
var latitude: Double // 緯度
let longitude: Double // 經度
}
let appHeadQuarterLocation: Location = Location(latitude: 37.3230, longitude: -122.0322) // 定義Apple總部的位置
let googleHeadQuarterLocation: Location = Location(latitude: 37.4220, longitude: -122.0841) // 定義Google總部的位置
appHeadQuarterLocation.latitude // 查看Apple的經度屬性值
googleHeadQuarterLocation.longitude // 查看Google的緯度屬性值
appHeadQuarterLocation.latitude = 0 // 此處會報錯,因為結構體Location所定義的latitude為常量
appHeadQuarterLocation = googleHeadQuarterLocation // 此處也會報錯,因為appHeadQuarterLocation定義的是一個常量值
```
## 獲取屬性值
```
appleHeadQarterLocation.latitude
googleHeadQarterLocation.longitude
```
## 結構體的嵌套使用
結構體的使用時非常靈活的,我們不僅僅可以使用之前學習使用的基本數據類型`Int`、`String`、`Doubel` 等等,還可以在結構體內部嵌套結構體。如下
```
struct Place{
let location: Location // 該結構體是上文中設置的Location的類型
var name: String // 字符串類型的屬性
}
var googleHeadQarter = Place( location: googleHeadQuarterLocation , name: "google" )
googleHeadQarter.location.latitude // 37.422
```
## 結構體之構造函數
```
struct Location {
var latitude: Double = 0 // 結構體可以賦初始值
var longitude: Double = 0
}
Location() // 都不填寫的話,就使用初始值
let appleHeadQarterLocation = Location( latitude: 37.3230,longitude: -122.0322)
```
> 結構體的初始化時默認將結構體內所有的屬性值補齊;
> 結構體默認初始化函數內傳遞的屬性值不是任意的,根據定義的時候的順序傳遞
> 對于結構體的屬性可以在定義結構體的時候賦初始值,并將其設置為變量,也就是使用 `var` 關鍵字聲明屬性
### 自定義構造函數
使用 `init` 定義函數,不需要寫 func,也不需要返回值。
```
struct Location {
let latitude: Double
let longitude: Double
// 自定義構造函數,不需要寫 func,也不需要返回值。
init (coordinateString: String) {
let commaIndex = coordinateString.range(of: ",")!.lowerBound
let firstElement = coordinateString.substring(to: commaIndex)
let secondElement = coordinateString.substring(from: coordinateString.index(after: commaIndex))
self.latitude = Double(firstElement)! // 定義的常量可以在這里初始化
self.longitude = Double(secondElement)!
}
}
let location = Location(coordinateString: "37.3230,-122.0322")
```
如上代碼中自定義了結構體的構造函數,則不再允許以下面這種方式初始化結構體。
```
let location2 = Location(latitude: 37.3230, longitude:-122.0322)
```
這時我們可以再定義一個構造函數進行結構體的初始化。**同時也建議這樣做。**
```
struct Location {
let latitude: Double
let longitude: Double
// 自定義構造函數,同時省略參數名,但是為了程序的可讀性不建議這么做。
init (_ coordinateString: String){
let commaIndex = coordinateString.range(of: ",")!.lowerBound
let firstElement = coordinateString.substring(to: commaIndex)
let secondElement = coordinateString.substring(from: coordinateString.index(after: commaIndex))
self.latitude = Double(firstElement)!
self.longitude = Double(secondElement)!
}
// 自定義構造函數2
init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
}
let location = Location("37.3230,-122.0322")
let location2 = Location(latitude: 37.3230, longitude: -122.0322)
```
> 上面的結構體構造函數中使用了可選型的強制解包,這將導致程序的不可預料的錯誤,這時我們可以使用 `guard` 關鍵詞進行判定。
對于結構體而言,結構體中的屬性可以賦初始值,也可以在`init`函數中賦值,但是可選性卻例外,我們可以在結構體外部對可選型的值進行修改
```
struct Location {
let latitude: Double
let longitude: Double
var placeName: String?
// 自定義構造函數,同時省略參數名,但是為了程序的可讀性不建議這么做。
init (_ coordinateString: String){
let commaIndex = coordinateString.range(of: ",")!.lowerBound
let firstElement = coordinateString.substring(to: commaIndex)
let secondElement = coordinateString.substring(from: coordinateString.index(after: commaIndex))
self.latitude = Double(firstElement)!
self.longitude = Double(secondElement)!
}
// 自定義構造函數2
init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
// 我們可以寫一個全參數的初始化init方法,給可選性賦值
init(latitude: Double, longitude: Double, placeName: String?) {
self.latitude = latitude
self.longitude = longitude
self.placeName = placeName
}
}
let location3 = Location(latitude: 37.3230, longitude: -122.0322, placeName: "Apple Head Quarter")
```
### 可失敗的構造函數
使用 `init?` 關鍵字定義可失敗的構造函數,叫做Failable-Initializer。在失敗的構造函數中,我們可以大膽的返回`nil`值,而不會構造函數內解包出錯等問題使程序拋出錯誤而終止程序。
```
struct Location {
let latitude: Double
let longitude: Double
init(){
self.latitude = 0.0
self.longitude = 0.0
}
// 自定義構造函數1
// 可失敗的構造函數
init?(coordinateString: String){
// 使用 guard 進行程序保衛性判定,防止 nil 值.
guard let commaIndex = coordinateString.range(of: ",")?.lowerBound,
let firstElement = Double(coordinateString.substring(to: commaIndex)),
let secondElement = Double( coordinateString.substring(from: coordinateString.index(after: commaIndex)) )
else{
return nil // `init?` 可是失敗的構造函數支持返回nil
}
self.latitude = firstElement
self.longitude = secondElement
}
// 自定義構造函數2
init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
}
// 所以大多數情況下建議將構造函數的參數與結構體的屬性保持一致
let location = Location(coordinateString: "37.3230,-122.0322")
let location2 = Location(coordinateString: "37.3230,-122.0322")!
let location3 = Location(coordinateString: "37.3230&-122.0322")
let location4 = Location(coordinateString: "apple,-122.0322")
let location5 = Location(coordinateString: "37.3230,apple")
let location6 = Location(coordinateString: "Hello, World!")
```
### 在結構體中創建多個構造函數
對于結構體,沒有便利的構造函數和指定的構造函數之分,如果結構體中的函數需要調用自身的其它構造函數,內部邏輯直接使用`self.init(...)`進行調用。
```
struct Point {
var x = 0.0
var y = 0.0
}
struct Size {
var width = 0.0
var height = 0.0
}
struct Rectangle {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + size.width / 2
let centerY = origin.y + size.height / 2
return Point(x: centerX, y: centerY)
}
set {
origin.x = newValue.x - size.width / 2
origin.y = newValue.y - size.height / 2
}
}
// 構造函數0
init(origin: Point, size: Size) {
self.origin = origin
self.size = size
}
// 構造函數1調用其它構造函數,不需要任何關鍵字進行修飾,內部直接使用 self.init() 進行調用
init(center: Point, size: Size) {
let originX = center.x - size.width / 2
let originY = center.y - size.height / 2
self.init(origin: Point(x: originX, y: originY), size: size) // 調用自身的指定構造函數
}
var area: Double {
return size.width * size.height
}
}
```
## 在結構體中寫方法
```
struct Location {
let latitude: Double
let longitude: Double
init(){
self.latitude = 0.0
self.longitude = 0.0
}
// 自定義構造函數
// 可失敗的構造函數
init?(coordinateString: String){
// 使用 guard 進行程序保衛性判定,防止 nil 值.
guard let commaIndex = coordinateString.range(of: ",")?.lowerBound,
let firstElement = Double(coordinateString.substring(to: commaIndex)),
let secondElement = Double( coordinateString.substring(from: coordinateString.index(after: commaIndex)) )
else{
return nil
}
self.latitude = firstElement
self.longitude = secondElement
}
// 自定義構造函數2
init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
// 定義沒有參數也沒有返回值的方法
func printLocation() {
print("The Location is \(self.latitude),\(self.longitude)")
}
// 定義有返回值的方法
func isNorth() -> Bool {
return self.latitude > 0
}
// 方法調用結構體中其他方法
func isSouth()-> Bool {
return !self.isNorth()
}
// 方法接收參數
func distanceTo(location: Location) -> Double {
return sqrt(pow(self.latitude , location.latitude) + pow(self.longitude, location.longitude))
}
}
let appleHeadQuarterLocation = Location(latitude: 37.3230 , longitude: -122.0322)
appleHeadQuarterLocation.printLocation() // The Location is 37.323,-122.0322
appleHeadQuarterLocation.isNorth() // true
appleHeadQuarterLocation.isSouth() // false
let googleHeadQuarterLocation = Location(coordinateString: "37.4220,-122.0841")
appleHeadQuarterLocation.distanceTo(location: googleHeadQuarterLocation!) // nan
```
## 結構體是值類型
`Value Type`值類型,賦值即是拷貝。
```
struct Point {
var x = 0
var y = 0
}
var p1 = Point()
var p2 = p1 // 賦值 將 p1 的值拷貝了一份賦值給 p2
p2.x += 1
p2.x // 1
p1.x // 0 原始值并沒有改變。
```
> `Array` , `Dirctionary` , `Set` 是結構體;
`Int` , `Float` , `Double` , `Bool` , `String` 也都是結構體,所以他們都是值類型的數據結構。
- 學習筆記
- 基礎
- 基本類型之整型
- 基本類型之浮點型
- 基本類型之布爾類型以及簡單的 if 語句
- 基礎類型之元組
- 基本類型之其他
- 運算符
- 基礎運算符
- 比較運算符、邏輯運算符
- 三元運算符
- 范圍運算符for-in
- 邏輯控制
- 循環結構
- 選擇結構
- 字符串
- Character和Unicode
- String.index 和 range
- 可選型
- 容器類
- 數組初始化
- 數組基本操作
- 字典初始化
- 字典基本操作
- 集合初始化
- 集合基本操作
- 函數
- 閉包
- 枚舉
- 結構體
- 類
- 文檔注釋
- 屬性和方法
- 下標和運算符重載
- 拓展和泛型
- 協議
- 其他
- Swift 3.0 For 循環
- Swift 隨機數的生成
- IOS開發玩轉界面 UIKit
- UILable 文本顯示控件
- UIButton 簡單的交互控件
- UIImageView 圖片控件
- UISearchBar 搜索控件