Swift基礎知識(二)

閉包(Closures)
 * 閉包是自包含的功能代碼塊,可以在代碼中使用或者用來作爲參數傳值。 
 * 在Swift中的閉包與C、OC中的blocks和其它編程語言(如Python)中的lambdas類似。 
 * 閉包可以捕獲和存儲上下文中定義的的任何常量和變量的引用。這就是所謂的變量和變量的自封閉, 
 * 因此命名爲”閉包“("Closures)").Swift還會處理所有捕獲的引用的內存管理。


閉包的形式有:
     * (1)全局函數都是閉包,有名字但不能捕獲任何值。 
     * (2)嵌套函數都是閉包,且有名字,也能捕獲封閉函數內的值。 
     * (3)閉包表達式都是無名閉包,使用輕量級語法,可以根據上下文環境捕獲值。
     *   (4) 全局函數和嵌套函數其實就是特殊的閉包
Swift中的閉包有很多優化的地方:
 * (1)根據上下文推斷參數和返回值類型 
 * (2)從單行表達式閉包中隱式返回(也就是閉包體只有一行代碼,可以省略return) 
 * (3)可以使用簡化參數名,如$0, $1(從0開始,表示第i個參數...) 
 * (4)提供了尾隨閉包語法(Trailing closure syntax) 

以sort 方法爲例 講解閉包的
sort(@noescape isOrderedBefore: (Self.Generator.Element, Self.Generator.Element) -> Bool) -> [Self.Generator.Element]

1.在sort方法中調用函數
let arr = ["a", "b", "c", "m", "e", "d"]
// 排序字符串數組
func sortString(a: String, b: String) -> Bool {
    return a < b
}
print(arr.sort(sortString))

2.使用閉包形式
完整閉包寫法是在花括號內有參數列表和返回值,用關鍵字in表明閉包體的開始
 閉包的語法:
 {(參數)-> 返回值類型 in 功能代碼}

let arr1 = ["a", "e", "q", "b"]
print(arr1.sort({ (a: String, b: String) -> Bool in
    return a > b
}))

簡化閉包
1.根據上下文推斷類型
參數列表都沒有指明類型,也沒有指明返回值類型,這是因爲swift可以根據上下文推測出 
print(arr1.sort({ a, b in
    return a < b
}))

2.單表達式閉包隱式返回
因爲閉包體只有一行代碼,可以省略return
print(arr1.sort({ a, b in
    a > b
}))

3.參數名稱縮寫(($i,i=0,1,2...從0開始的)Swift會推斷出閉包需要兩個參數)
print(arr1.sort({$0 > $1}))

4.運算符函數
// 閉包在某些情況下可以直接使用運算符函數
print(arr1.sort(>))

5.尾隨閉包
    * 尾隨閉包(Trailing Closures) 
    * 如果函數需要一個閉包參數作爲參數,且這個參數是最後一個參數,而這個閉包表達式又很長時, 
    * 使用尾隨閉包是很有用的。尾隨閉包可以放在函數參數列表外,也就是括號外。如果函數只有一個參數, 
    * 那麼可以把括號()省略掉,後面直接跟着閉包。
// 尾隨閉包
func tailClosure(a: Int, b: (Int, Int) -> Int){
    print("\(a) \(b(1,2))")
}
// 調用函數
// 尾隨閉包的語法:函數(){閉包}
tailClosure(10) { (a: Int, b: Int) -> Int in
    return a + b
}

6.捕獲值 - 函數嵌套
 * 閉包可以根據環境上下文捕獲到定義的常量和變量。閉包可以引用和修改這些捕獲到的常量和變量, 
 * 就算在原來的範圍內定義爲常量或者變量已經不再存在(很牛逼)。 
 * 在Swift中閉包的最簡單形式是嵌套函數。 
// 函數的嵌套是一種特殊形式的閉包  函數類型作爲返回值   sum可以保留上次的結果
func increametMaker(cnt: Int) -> () -> Int {
    var sum = 0
    func increamet() -> Int {

        // 在子函數中可以操作外部函數的變量
        sum += cnt

        return sum
    }
    return increamet
}


let maker = increametMaker(10)
print(maker())


7.閉包是引用類型
// 閉包是引用類型  --  傳址
let maker1 = maker  // 指向同一塊地址
print(maker1())



類和結構體
1.類和結構體的對比
Swift 中類和結構體有很多共同點。共同處在於:
    •   定義屬性用於儲存值
    •   定義方法用於提供功能
    •   定義下標用於通過下標語法訪問值
    •   定義初始化器用於生成初始化值
    •   通過擴展以增加默認實現的功能
    •   符合協議以對某類提供標準功能

與結構體相比,類還有如下的附加功能:
    •   繼承允許一個類繼承另一個類的特徵
    •   類型轉換允許在運行時檢查和解釋一個類實例的類型
    •   取消初始化器允許一個類實例釋放任何其所被分配的資源
    •   引用計數允許對一個類的多次引用

2.類和結構體的語法定義
通過關鍵字classstruct來分別表示類和結構體,並在一對大括號中定義它們的具體內容
class SomeClass {
    // class definition goes here
}
struct SomeStructure {
    // structure definition goes here
} 
類名和結構體名的命名規範:大駝峯

3.類和結構體實例化
結構體和類都可以使用初始化器語法來生成新的實例。初始化器語法的最簡單形式是在結構體或者類的類型名稱後跟隨一個空括弧,如Resolution()或VideoMode()。但是結構體還有另外一個初始化方式:成員逐一初始化器

var p = Person()
var s = Student(name: "xiaowang", age: 30)

4.屬性訪問 —— 以點語法的形式訪問屬性

5.結構體和枚舉是值類型  類是引用類型
值類型被賦予給一個變量,常數或者本身被傳遞給一個函數的時候,實際上操作的是其的拷貝。
引用類型在被賦予到一個變量,常量或者被傳遞到一個函數時,操作的並不是其拷貝。因此,引用的是已存在的實例本身而不是其拷貝。

枚舉
enum ASCIIControlCharacter: Character {
    case Tab = "\t"
    case LineFeed = "\n"
    case CarriageReturn = "\r"
}

值類型
           let hd = Resolution(width: 1920, height: 1080)
        var cinema = hd
        cinema.width = 2048
        print("cinema is now \(cinema.width) pixels wide")
        // 輸出 "cinema is now 2048 pixels wide"
        print("hd is still \(hd.width ) pixels wide")
        // 輸出 "hd is still 1920 pixels wide"

引用類型
        let tenEighty = VideoMode()
        tenEighty.resolution = hd
        tenEighty.interlaced = true
        tenEighty.name = "1080i"
        tenEighty.frameRate = 25.0
        let alsoTenEighty = tenEighty
        alsoTenEighty.frameRate = 30.0
        print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
        // 輸出 "The frameRate property of theEighty is now 30.0"

6.恆等運算符
如果能夠判定兩個常量或者變量是否引用同一個類實例將會很有幫助。爲了達到這個目的,Swift 內建了兩個恆等運算符:
    •   等價於 ( === )
    •   不等價於 ( !== )

類中的方法
1.實例方法和類方法
2.實例方法的調用
3.self 屬性
// 實例方法的使用
class Dog {
    var name: String = ""
    var age: Int = 0
    // 創建實例方法
    func run() {
        print("running")
    }
    // 傳參
    func wang(name: String) {
        // 在實力方法中調用屬性時 必須使用self點語法調用 不然報錯
        // 此時的self爲當前類對象
        self.name = name
    }
    // 類方法
    static func jump() {
        print("jumping")
    }
}

結構體中的方法
1.值類型實例方法中修改屬性
2.在可變方法中給 self 賦值
// 結構體
struct Cat {
    var name: String = ""
    var age: Int = 0
    // 創建實例方法
   func voice() {
        print("喵~")
    }
    mutating func eat(name: String)  {
        // 在結構體中的實例方法 不能直接操作外面的屬性 若想操作需加關鍵字mutating
        self.name = name
    }
    // 創建可變方法
    mutating func run(){
        // 在可變方法中 可以修改self的值
        self = Cat(name: "金錢豹", age: 5)
    }
    // 類方法
    static func jump() {
        print("jumping")
    }
}


屬性 — 存儲屬性和計算屬性
1.存儲屬性
一個存儲屬性就是存儲在特定類或結構體的實例裏的一個常量或變量,存儲屬性可以是變量存儲屬性(用關鍵字var定義),也可以是常量存儲屬性(用關鍵字let定義)。

// 類中的存儲屬性
class People {
    // 聲明存儲屬性
    var name: String = ""
    // let聲明的屬性 在外界無法通過點語法調用修改值
    let age: Int = 0
}
// 這裏let可以 因爲指向的地址
let people = People()
people.name = "山治"
// 不允許改變常量的值
//people.age = 30
//print(people.age)
print(people.name)
// 結構體裏面的存儲屬性
struct Tiger {
    var name: String = ""
    // 在結構體中 通過let聲明的屬性 不能通過點語法調用修改值
    let age: Int = 0
}
// 值引用
let ti = Tiger()
// 若結構體對象
// ti.name = "wang"
//ti.age = 30


延遲存儲屬性
// 延遲存儲屬性
class Monkey {
    var arr = [Int]()
    // 直到調用該屬性時 纔會進行屬性的運算  類似於懶加載
    lazy var name: String = ""

}
var mo = Monkey()
mo.arr.append(1)
mo.arr.append(2)
// 當調用name時 CPU纔會對name屬性進行運算
mo.name = "孫悟空"
print(mo.name)


2.計算屬性
計算屬性不直接存儲值,而是提供一個 getter 來獲取值,一個可選的 setter 來間接設置其他屬性或變量的值。
便捷 setter 聲明 — 如果計算屬性的 setter 沒有定義表示新值的參數名,則可以使用默認名稱newValue

// 實現功能:創建矩形對象 將原點設置 寬高設置 查找中心點
struct Point {
    var x = 0
    var y = 0
}

struct Size {
    var width = 0
    var height = 0
}

// 聲明矩形類
class Rect {
    var point = Point()
    var size = Size()
    // 計算型屬性 需要通過set/get方法的
裝實現
    var center: Point {
        // centerPoint 等號右邊的值  可用newValue代替
        set {
            point.x = newValue.x - size.width / 2
            point.y = newValue.y - size.height / 2
        }
        get {
            let centerX = point.x + size.width / 2
            let centerY = point.y + size.height / 2
            return Point(x: centerX, y: centerY)
        }
    }

}
var rect = Rect()
rect.point.x = 0
rect.point.y = 0
rect.size.width = 10
rect.size.height = 20
print(rect.center)



只讀計算屬性
只有 getter 沒有 setter 的計算屬性就是隻讀計算屬性。只讀計算屬性總是返回一個值,可以通過點運算符訪問,但不能設置新的值。

// 聲明只讀的屬性
class School {
    // 學校名稱
    var name: String = ""
    // 班級個數
    var classNumber = 0
    // 每個班級人的個數
    var gradeNumber = 0

    // 查看學校總人數 (只讀屬性)
    // 語法  聲明變量 變量名:類型{return }
    var totalStudents: Int {
        return classNumber * gradeNumber
    }
}
var sc = School()
sc.name = "qhfz"
sc.classNumber = 20
sc.gradeNumber = 10
//sc.totalStudents = 90
print("\(sc.name)學校共有\(sc.totalStudents)學生")


類屬性語法 與 屬性監視器
// 屬性的監視器
class Hotel {
    // 監聽屬性是否發生改變
    var name = "7tian" {
        willSet {
           print(name)
        }
        didSet {
           print(name)
        }
    }

    // 類屬性
    static var age: Int = 10
}

var hotel = Hotel()
hotel.name = "hanting"
// 調用類屬性
Hotel.age = 50
print(Hotel.age)



繼承
定義一個基類
子類生成
子類可以繼續繼承子類
重寫方法
防止重寫

// 繼承  當用final修飾類時  代表該類不能被繼承
class Car {
    var name = ""
    var size = 0

    func run()  {
        print("running^")
    }

    // 防止重寫 final 防止子類對父類中方法的重寫
    final func stop() {
        print("stop")
    }
}
// 繼承父類Car
class Bus: Car {
    var number = 0
}
// 沒有多繼承 但是可以多層繼承
var bus = Bus()
// 繼承的屬性
bus.name = "Bus"
bus.size = 100
// 繼承的方法
bus.run()

// 派生的屬性
bus.number = 81


// 多層繼承可以繼承基類中的屬性和方法
// 繼承自Bus
class RedBus: Bus {
    var color = "red"
    // 重寫父類的方法 override
    override func run() {
        // 調用父類的方法
        super.run()
        print("RedBus-Running^^")
    }
}

var redBus = RedBus()
redBus.name = "RedBus"
redBus.run()


構造和析構
構造函數
不帶外部名的構造器參數
構造函數參數的內部名稱和外部名稱
構造過程中常量屬性的修改
// 構造函數的使用
class Animal {
    func run() {
        print("running^^^^^")
    }

    var name = ""
    var age = 0
    // 在構造方法中修改常量的值時  該值不能被初始化
    let num: Int

    // 創建構造方法給屬性賦值
    // 當類被初始化時(實例化) 第一個調用的函數就是構造函數
//    init() {
//        self.name = "cat"
//        print("在構造方法中給屬性賦值")
//    }

    // 帶有參數的構造函數
//    init(a: String){
//        self.name = a
//       
//    }

    // 帶有外部參數的構造函數
//    init(name param1: String, age param2: Int){
//        self.name = param1
//        self.age = param2
//    }

    // 隱藏所有外部參數
//    init(_ param1: String, _ param2: Int) {
//        self.name = param1
//        self.age = param2
//    }
    // 在構造函數中可以訪問沒有被初始化的常量屬性
    init(n: Int) {
        self.num = n
    }

}
//var animal = Animal()
//var animal = Animal(a: "dog")
//var animal = Animal(name: "monkey", age: 5)
//var animal = Animal("chicken",2)
var animal = Animal(n: 100)
 print(animal.num)


默認構造器
結構體的逐一成員構造器
// 默認的構造器
class Mouse {
    var name = ""

}
// 每個類中會有默認構造器 init(){}  不寫這行代碼 也會聯想小括號
var mouse = Mouse()


// 結構體的逐一成員構造器
struct Phone {
    var name = ""
    var age = 0
}
// 只有值類型的數據被實例化時 纔有逐一成員構造器
var phone = Phone(name: "iPhone", age: 4)


構造器的繼承和重寫
析構過程
// 構造器的繼承和重寫
class Table {
    var name = ""

    init(name: String){
        self.name = name
    }
}

class Desk: Table {
    override init(name: String) {
        // 重寫父類的構造方法 必須調用父類相關的構造方法
        super.init(name: name)
        print("重寫構造函數 千萬不要忘記super.init")
    }


    // 析構函數 -- 最後調用的方法  當ARC銷燬對象是調用析構函數
    deinit {
        print("deinit")
    }
}

var desk: Desk? = Desk(name: "Teacher Li Desk")
//打印結果: 重寫構造函數 千萬不要忘記super.init Teacher Li Desk
print(desk!.name)


// 調用析構函數  需將對象設置爲可選類型
desk = nil



只讀屬性類型的拓展
方法擴展
// 類別
extension Int {
    // 只能添加只讀類型的屬性
    var  a: Int {
       return self * self
    }

}
print(5.a)

class Duck {
    var name = ""

}

extension Duck {
    var age: String{
        return self.name + "qianfeng"
    }

    func run() {
        print("Dusk Running")
    }
}

var duck = Duck()
duck.run()
duck.name = "唐老鴨"
print(duck.age)

協議語法
協議中屬性的用法
協議中方法的用法
委託模式
// 協議
protocol Work: class {
    // 方法
    // 只需聲明  默認必須實現
    func teaching()
}
// 可選的協議方法
@objc protocol Teach {
    optional
    func say()
}
// 如果某個類既有父類 有需要遵守協議  class 類名:父類,協議,協議2.。。
class Worker: Work, Teach {

    // 實現協議中的方法
    func teaching() {
        print("協議中的方法")
    }

    @objc func say() {
        print("可選的協議方法")
    }
}


下標的使用
// 類中下標的使用
var arr = [2,3,1,7]
print(arr[2])
class Days {
    var days = ["Mon", "Tues", "wend", "Thir", "Fri"]
    // 類中創建下標
    // 下標可以有一個或者多個參數  有一個返回值
    subscript (a: Int) -> String {
        // get方法的封裝
        get {
            return days[a]
        }
    }

}
let days = Days()
// 通過下標訪問實例對象
let day = days[0]
print(day)


class Person {
    var arr = Array(count: 10, repeatedValue: 0)
    // 類中創建下標
    // 下標可以有一個或者多個參數  有一個返回值
    subscript (a: Int) -> Int {
        // set get方法的封裝
        set{
            arr[a] = newValue
        }
        get {
            return arr[a]
        }
    }

}

let person = Person()
person[0] = 200
print(person[0])
// 對類中的數組封裝起一個輔助的功能

可選鏈
// 可選鏈 - 通過連續的點語法調用
class Eye {
    var num = 2
}

class Student {
    // 設置可選類型  確定是以類的屬性的存在
    var eye: Eye? = Eye()
}

let st = Student()
// 至少有一個屬性的類型爲可選模式  手動將問號改爲歎號
print(st.eye!.num)

is類型檢測  Any與AnyObject
// 類型檢測
var a = 20
//if a is Int {
//    print("Int")
//}
// Any 任何類型
var objInt: Any = a
if objInt is Int {
    print("Int")
}
class A {
    var name = ""
}
class B: A {

}
class C {

}
var b = B()
// 判斷對象是否屬於類或者父類
// AnyObject 與id一致  可以指向任何實例,一般指向對象 Any是執行任何類型
var objB: AnyObject = b
if objB is A {
    print("B")
}

as! as? 類型轉換
// as!  as?  類型轉換

// 類類型做數據轉換  objB as! B 將objB該類型轉換成B類型
var obj = objB as! B

// 用as?做強制轉換  如果強制轉換成功 則是轉換的數據類型  如果轉換失敗,則是nil
var obj2 = objB as? C
if obj2 == nil {
    print("不能轉化爲該類型")
}


異常處理

// 異常處理
// 聲明異常
enum MyError: ErrorType {
    case Error0
    case OutOfRange
    case ErrorOther
}

func divide(a: Int, b: Int) throws -> Int {

    if b == 0 {
        // 拋出異常 : 1.首先將函數定義成throws 函數
        throw MyError.Error0
    } else {
         return a / b
    }

}
// 調用函數
// 拋出異常就必須接收異常
do {
    let rs = try divide(10, b: 0)
    print(rs)
} catch MyError.Error0 {
    print("除數不能爲0")
}
// 通過try! try? 調用throwing函數  
// try? 有異常爲nil  沒異常爲值
//let rs = try? divide(5, b: 0)
//if rs != nil {
//   print(rs!)
//} else {
//    print("youyichang-")
//}


// try! 在確定throwing函數肯定無異常時 可以通過try!調用



發佈了24 篇原創文章 · 獲贊 5 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章