# 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 搜索控件