1.可選類型和強制解包(?和!)
1> 可選類型(?)和強制解包(!)
在swift中,可選類型(?) 其根源是一個 枚舉型,裡面有 None 和 Some 兩種類型。其實所謂的 nil 就是 Optional.None , 非 nil 就是 Optional.Some.
可選類型是的資料如果不進行解包的話,它是一個 Optional 類型的資料,如果我們想要使用原來類型的資料,必須進行解包
// 定義一個Int類型的可選類型變量
var intNumber:Int? = 8
// 把這個類型類型的變量指派給另一個可選類型的變量
var intNumberOne:Int? = intNumber
print(intNumberOne)
// 列印出來以後是一個Optional類型的值,如果要取到8,必須對可選類型強制解包!
var intNumberTwo:Int = intNumber!
print(intNumberTwo)
列印結果:
2> 可選綁定
可選類型分為有值和沒值,如果可選類型的變量沒值時對其強制解包,程式就會崩潰 ,是以,強制解包是非常危險的
示例代碼
// 定義一個Int類型的可選類型變量
var intNumber:Int?
// 把這個類型類型的變量指派給另一個可選類型的變量
var intNumberOne:Int? = intNumber
print(intNumberOne)
var intNumberTwo:Int = intNumber!
print(intNumberTwo)
崩潰資訊:
如果不确定可選類型變量是否有值時,用可選綁定,不需要對可選類型強制解包
var Number:Int?
// ! 表示強制解包
//var NumberOne = Number! // 崩潰
//print(intNumberOne)
// 如果不确定可選類型變量是否有值時,用可選綁定,不需要對可選類型強制解包
if var intNumberTwo = Number {
print(intNumberTwo)
}
3> 隐式解析可選類型(!)
隐式解析可選類型和可選類型一樣,都是 有值 和 沒值(nil) 兩種結果,差別是指派時,隐式解析可選類型不需要強制解包。
// ! 隐式解析可選類型 : 有值,沒值(nil)
var intNum: Int! = 10
// 如果隐式解析可選類型的變量沒值,程式一樣會崩潰
//var intNumOne = intNum
//print(intNumOne)
// 可選綁定
if var intNumTwo= intNum {
print(intNumTwo)
}
2.結構體
1> 概述
Swift的結構體對比OC來說,可以添加初始化方法、可以遵守代理協定等
聲明一個結構體的格式:
struct + 結構體的名字 + {
聲明成員變量等
}
2> 聲明一個結構體代碼
1 // 1. 聲明一個結構體
2 struct Rect {
3 // 1.1 聲明結構體變量的屬性(存儲屬性)
4 var x:Float
5 var y:Float
6 var width:Float
7 var height:Float
8
9 // 1.2 聲明結構體屬性,要使用static
10 static var desciption:String?
11
12 // 1.3 聲明計算屬性(是用來專門計算結構體變量屬性的setter和getter方法,其本身并沒有存儲功能)
13 var centerX:Float {
14 // setter方法
15 set {
16 // x = newValue
17 x = x / 2
18 }
19
20 // getter方法,必須要寫的方法
21 get {
22 return x / 2
23 }
24 }
25
26 var centerY:Float {
27 get {
28 return y / 2
29 }
30 }
31
32 // 1.4 聲明方法
33 // 聲明一個結構體變量方法(相當于OC中的執行個體方法)
34
35 func frameInfor() {
36 print("x:\(x),y:\(y),width:\(width),height:\(height)")
37 }
38
39 // 聲明一個結構體方法(相當于OC中的類方法),使用static修飾
40 static func infor() {
41 print("這是結構體方法")
42 }
43
44 }
45
46 // 1. 根據結構體去定義一個結構體變量
47 var frame = Rect(x: 10, y: 10, width: 100, height: 100)
48
49 // 2. 通路結構體變量中的屬性
50 // 注意:結構體變量的屬性類型可以使用let去修飾,隻不過通路的時候不能對其進行修改
51 print(frame.x)
52
53 // 3. 通路結構體屬性
54 Rect.desciption = "我是結構體屬性"
55 print(Rect.desciption)
56
57 // 4. 通路計算屬性
58 frame.centerX = 20 // 這句話就相當于在調用centerX的setter方法
59
60 let value = frame.centerX // 這句話就相當于在調用centerX的getter方法
61
62 print(value)
63 print(frame.centerY)
64
65 // 5. 調用結構體變量方法
66 frame.frameInfor()
67 // 6. 調用結構體方法
68 Rect.infor()
3> 代碼解析
- 聲明一個結構體
聲明:代碼 1 - 44行,定義一個結構體變量和對結構體的相關操作:代碼46 - 68行
結構體不需要重寫初始化方法,直接可以定義一個結構體變量,代碼:46 - 47
- 聲明結構體變量的屬性(存儲屬性)
聲明:代碼 3 - 7行,通路:代碼49 - 50行
結構體變量的屬性可以是變量(var),也可以是常量(let),以上代碼以var為例
- 聲明結構體屬性
聲明:代碼 9 - 10行,通路:代碼53 - 55行
要使用 static 修飾,必須由 結構體 進行通路
- 聲明計算屬性
聲明:代碼 12 - 30行,通路:代碼57 - 63行
用來專門計算結構體變量屬性的 setter 和 getter 方法,其本身并沒有存儲功能,相當于OC中重寫的 setter 和 getter 方法
- 聲明結構體變量方法
聲明:代碼 33 - 37行,通路:代碼65 - 66行
相當于OC中的執行個體方法
- 聲明結構體方法
聲明:代碼 39 - 42行,通路:代碼67 - 68行
使用 static 修飾,相當于OC中的類方法
注意:類方法中隻能使用 類屬性,不能使用 對象屬性
3.類
類是人們建構代碼所用的一種通用且靈活的構造體。我們可以使用與結構體完全相同的文法規則來為類定義屬性(常量、變量)和添加方法。
我們通過關鍵字class來定義類,并在一對大括号中定義它們的具體内容:
class ClassName {
類的内部細節
}
2> 聲明一個父類和子類的代碼
1 class Person {
2 // 1.2 對象屬性
3 var name:String?
4 var age:Int?
5
6 // 1.3 聲明類屬性
7 static var introduce:String?
8
9 // 1.4 計算屬性
10 var value:Int {
11 set {
12 age = newValue // 在寫計算屬性的時候,一定不能出現self,不然會出現死循環
13 }
14
15 get {
16 return age!
17 }
18 }
19
20 // 1.1 構造初始化方法
21 init(name:String, age:Int) {
22 self.name = name
23 self.age = age
24 }
25 // 自定義初始化方法
26 init(name:String) {
27 self.name = name
28 }
29
30 // 1.5 聲明一個類方法
31 // 1.5.1 在類方法前面加上一個static修飾【雖然是一個類方法,但是該方法在子類中不能進行重寫】
32 static func sayHi() {
33 print(introduce) // 注意:隻能使用類屬性,不能使用對象屬性
34 }
35 // 1.5.2 在類方法前面加上class修飾【它是一個類方法,可以被子類重寫】
36 class func sayHello() {
37 print(introduce)
38 }
39
40 // 1.6 聲明一個執行個體(對象)方法
41 func sayHi1() {
42 print("我是執行個體方法")
43 }
44 }
45
46 // 1. 建立對象(注意:要初始化方法)
47 var per : Person = Person(name: "MBBoy", age: 20)
48
49 // 2. 通路類中的屬性
50 // ! 解包後為原有屬性(列印結果: MBBoy ),不解包為Optional屬性(列印結果 Optional(20) )
51 print(per.name!)
52 print(per.age)
53
54 // 3. 通路類屬性
55 Person.introduce = "我是xxx"
56
57 // 4. 通路計算屬性
58 per.value = 28
59 print(per.value)
60
61 // 5. 通路類方法
62 Person.sayHello()
63 Person.sayHi()
64
65 // 6. 通路執行個體方法
66 per.sayHi1()
67
68 // 定義一個子類Student,繼承Person
69 // Swift不支援多繼承
70 class Student:Person {
71 // 重寫父類的方法
72 // 重寫父類的類方法
73 override class func sayHello() {
74 print("我是子類Student,重寫父類的類方法")
75 }
76
77 // 重寫父類的執行個體方法
78 override func sayHi1() {
79 print("我是子類Student,重寫父類的執行個體方法")
80 }
81 }
82
83 // 初始化Student對象
84 var stu = Student(name: "張三", age: 25)
- 聲明父類
聲明:代碼 1 - 44行,初始化對象和對對象的相關操作:46 - 66行
- 重寫初始化方法
重寫:代碼 20 - 28行,建立對象:代碼 46 - 47行
與結構體不同,類的初始化方法必須重寫,不然不能建立對象
- 聲明對象的屬性(執行個體屬性)
聲明:代碼 2 - 4行,通路:代碼49 - 52行
對象的屬性可以是變量(var),也可以是常量(let),以上代碼以var為例
- 聲明類屬性
聲明:代碼 6 - 7行,通路:代碼54 - 55行
要使用 static 修飾,必須由 類名 進行通路
聲明:代碼 9 - 18行,通路:代碼57 - 59行
在寫計算屬性的時候,一定不能出現 self ,不然會出現死循環
- 計算屬性與存儲屬性
· 存儲屬性就是 類 或 結構體 裡 定義的變量(或常量)。存儲屬性可以是變量存儲屬性(用關鍵字 var定義),也可以是常量存儲屬性(用關鍵字let定義)。
· 除存儲屬性外,類、結構體和枚舉可以定義計算屬性。計算屬性不直接存儲值,而是提供一個 getter 和一個可選 的 setter,來間接擷取和設定其他屬性或變量的值。
- 聲明類方法
聲明:代碼 30 - 38行,通路:代碼61 - 63行
兩種方式:
使用 class 修飾,它是一個類方法,可以被子類重寫
使用 static 修飾,雖然是一個類方法,但是該方法在子類中不能進行重寫
- 聲明執行個體(對象)方法
聲明:代碼 40 - 43行,通路:代碼65 - 66行
- 聲明子類
聲明:代碼 68 - 81行
和OC相似,單繼承,可以遵循協定,兩者同時存在時,先在:後面寫父類,後寫協定,用逗号隔開
重寫父類方法,使用 override 關鍵字
4. 值類型和引用值類型的差別
值類型
- 該類型的每個執行個體持有資料的副本,并且該副本對于每個執行個體來說是獨一無二的一份,比如結構體(struct)、枚舉(enum)、元組(tuple)都是值類型
- 值傳遞隻是單純的将資料拷貝一份,指派給一個同類型的變量,兩個變量互不影響,一個值發生改變,另一個不會發生任何變化
引用值類型
- 該類型的執行個體共享資料唯一的一份副本(在native層面說的話,就是該類型的每個執行個體都指向記憶體中的同一個位址),比如類(class)就是引用類型。
- 值傳遞是将一個變量的位址指派給另一個變量,當一個值發生改變,兩個值同時改變
struct animal { // 值類型
var name:String?
var age:Int?
init(name:String, age:Int) {
self.name = name
self.age = age
}
}
var dog = animal(name: "貝貝", age: 3)
var dog1 = dog // 此時将dog的資料拷給dog1
dog.name = "歡歡"
print("dog.name:\(dog.name!), dog1.name:\(dog1.name!)")
// 引用值類型
class animal {
var name:String?
var age:Int?
init(name:String, age:Int) {
self.name = name
self.age = age
}
}
var dog = animal(name: "貝貝", age: 3)
var dog1 = dog // 此時将dog的資料拷給dog1
dog.name = "歡歡"
print("dog.name:\(dog.name!), dog1.name:\(dog1.name!)")
列印結果:
值類型與引用類型使用情形
- 使用值類型的情形:
使用 == 運算符比較執行個體資料的時候。
你想單獨複制一份執行個體資料的時候。
當在多線程環境下操作資料的時候。
- 使用引用類型(比如class)的情形:
當使用 === 運算符判斷兩個對象是否引用同一個對象執行個體的時候。
當上下文需要建立一個共享的、可變的對象時。
5.協定
- 協定定義了一個藍圖,規定了用來實作某一特定工作或者功能所必需的方法和屬性
- 類,結構體或枚舉類型都可以遵循協定,并提供具體實作來完成協定定義的方法和功能
- 任意能夠滿足協定要求的類型被稱為遵循(conform)這個協定
2> 聲明協定
- @objc 修飾的協定,其中的方法可以聲明成可選實作(使用optional修飾)
@objc protocol SayHelloDelegate {
optional func sayHello()
}
- 聲明一個所有函數都必須實作的協定。
protocol DescriptionDelegate {
func description() -> String
static func aClassMethod() ->String//表示聲明了一個類方法
}
3> 遵守協定
類遵守協定,直接寫在本類名後面的冒号的後面,使用 "," 号分隔
class ErShiXiong: NSObject,SayHelloDelegate, DescripationDelegate {
// 必須實作的協定方法,就必須實作,否則會報錯
func test() {
}
// 可選協定方法,會報警告,可以添加 @objc, 或者繼承NSObject
@objc func test1() {
}
}
6.擴充(Extension)
extension + 類名(結構體名字)可以對一個類和結構體擴充方法,類似于 OC 的 Category 類目
extension 可以多次對一個類進行擴充,也可以給一個類擴充協定方法
2> 使用Extension給類擴充一個方法
extension ErShiXiong {
func eat() {
print("吃飯")
}
}
let er = ErShiXiong()
er.eat()
3> 給類擴充實作協定
//先聲明一個協定MyProtocol
@objc protocol MyProtocol {
optional func test() //該法方法可選實作
func test()
}
extension ErShiXiong: MyProtocol {
func test() {
print("Hello")
}
}
7.閉包
1> 概述
- 閉包是自包含的函數代碼塊,可以在代碼中被傳遞和使用。 Swift 中的閉包與 C 和 Objective-C 中的 代碼塊(block)以及其他一些程式設計語言中的 匿名函數 比較相似。
- 閉包可以捕獲和存儲其所在上下文中任意常量和變量的引用。 這就是所謂的閉合并包裹着這些常量和變量,俗稱閉包。。
2> 文法形式
{
(參數)-> 傳回值類型 in
執行語句
}
3> 閉包的使用(五種方式)
// 聲明
var myBlock : ((num1:Int, num2:Int) ->Int)
// 第一種使用方式
myBlock = {
(num1:Int,num2:Int)->Int in // 切記不能忘記 in
return num1 > num2 ? num1 : num2
}
// 第二種使用方式
myBlock = {
(num1,num2)->Int in // 切記不能忘記 in
return num1 > num2 ? num1 : num2
}
// 第三種使用方式(常用,見名知意)
myBlock = {
num1, num2 in
return num1 > num2 ? num1 : num2
}
// 第四種使用方式
myBlock = {
num1, num2 in num1 > num2 ? num1 : num2
}
// 第五種使用方式
myBlock = {
$0 > $1 ? $0 : $1
}
let max = myBlock(num1: 88, num2: 69)
print(max)
4> 閉包傳值(和OC中的Block傳值類型)
需求:建立一個工程,勾選swift語言,建立兩個控制器給第一個控制器添加導航控制器,點選按鈕push到第二個控制器,在第二個頁面添加個按鈕,點選傳回第一個頁面,并将第二頁textField中輸入的值傳到第一頁,并在第一頁的textField中顯示
在第二頁聲明參數為String,無傳回值的閉包變量,用于傳值
class SecondViewController: UIViewController {
// 聲明參數為String,無傳回值的閉包變量
var block = {
(str: String) -> Void in
}
...
}
在第二頁的傳回按鈕的方法調用閉包
fun buttonAction (){
// 閉包調用
self.block!("block傳回值")
self.navigationController?.popViewControllerAnimated(true)
}
在第一頁的push按鈕中實作閉包
fun pushAction (){
let secondVC: SecondViewController = SecondViewController()
secondVC.view.backgroundColor = UIColor.orangeColor()
// 閉包實作,拿到傳回的值
secondVC.block = {
(str: String) -> Void in
self.textField?.text = str
}
self.navigationController?.pushViewController(secondVC, animated: true)
注意:在class類中聲明變量 最好立刻初始化 或 定義為可選變量,不然該類必須重寫初始化方法