天天看點

【原】iOS學習之Swift之文法2(精簡版)

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類中聲明變量 最好立刻初始化 或 定義為可選變量,不然該類必須重寫初始化方法