1. 程式人生 > >五、swift3.0閉包和懶載入

五、swift3.0閉包和懶載入

一、閉包的使用
與 OC中的Block類似,閉包主要用於非同步操作執行完成後的程式碼回撥,網路訪問結果以引數的形式傳遞給呼叫方
在OC中block是匿名的函式;在swift中閉包是特殊的函式
回撥的特點:以引數回撥處理結果;返回值為Void

1、閉包的定義

閉包 = { (行參) -> 返回值 in
    // 程式碼實現
}

//1、定義一個常量記錄函式
func sum(n : Int, m : Int) -> Int {
    return m + n
}
//swift中函式本身就可以當作引數被定義和傳遞
let f = sum

f(10, 20)

//2、定義一個最簡單的閉包,() -> () 沒有引數,沒有返回值
let p = {
    print("hhah")
}
p()

//3、定義一個帶引數的閉包
//通過“in”關鍵字,對閉包的定義和實現進行分割,沒有引數沒有返回值可以省略in
// {形參列表   返回值型別  -> in  實現程式碼}
let p2 = {(x : Int) -> () in
    print(x)
}
p2(123)


//4、定義一個帶引數和返回值的閉包
let p3 = {(x : Int) -> Int in
    return x + 10
}
print(p3(23))
let p1 = { () -> () in
    print("沒有引數,沒有返回值的閉包")
}

let p2 = {
    print("沒有引數,沒有返回值的閉包簡寫")
}

//注:在OC中{}用來分隔作用域的,而在swift中,表示一個閉包

2、閉包的使用
//作為函式的返回值,進行回撥操作
class Human: NSObject {
    
    func loadData(callBack : @escaping () -> ()) {
        DispatchQueue.global().async { () -> Void in
            
            print("載入資料的執行緒:\(Thread.current)");
            
            DispatchQueue.main.async(execute: {
                
                print("更新UI的執行緒:\(Thread.current)")
                
                callBack()
            })
        }
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    
    let per = Human()
    
    per.loadData {
        print("獲取到資料,重新整理UI")
    }
}

//結果:
載入資料的執行緒:<NSThread: 0x60000006c680>{number = 3, name = (null)}
更新UI的執行緒:<NSThread: 0x6000000639c0>{number = 1, name = main}
獲取到資料,重新整理UI

3、尾隨閉包
如果一個函式的最後一個引數是閉包,則函式的引數可以提前結束,最後一個引數直接使用{}包裝閉包的程式碼
let per = Human()
        
        //1、正常呼叫物件的函式
        per.loadData(callBack: { () -> () in
            print("獲取到資料,重新整理UI")
        })
        
        //2、尾隨閉包的改進:函式的引數可以提前結束,後面直接跟上一個閉包包裝程式碼
        per.loadData() { () -> () in
            print("獲取到資料,重新整理UI")
        }
        
        //3、簡寫
        per.loadData {
            print("獲取到資料,重新整理UI")
        }

4、閉包的迴圈引用
class ViewController: UIViewController {
    
    //定義一個閉包屬性
    var completion : (() -> ())?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //如果記錄了閉包屬性,然後在閉包中又使用了self,則產生了迴圈引用
        //解決閉包中的迴圈引用
        //方法一:
        //weak只能修飾var,不能修飾let,因為如果weak的指標在執行時會被修改,會自動設定為nil
        weak var weakself = self
        loadData {
            //!:強制解包,必須有值,如果解包的值為nil,則崩潰
            //?:可選解包,如果解包為nil,則不會執行後面的內容
            print(weakself?.view ?? "")
        }
        
        //方法二:推薦
        //[weak self]表示閉包中的self都是若引用
        loadData { [weak self] in
            print(self?.view ?? "")
        }
        
        //方法三:類似OC中的unsafe_unretained,常用
        //[unowned self]表示閉包中的self為assign,如果self被釋放,則指標地址不會被釋放,容易導致出現野指標
        loadData { [unowned self] in
            print(self.view)
        }
    }
    
    func loadData(complete: @escaping () -> ()) -> () {
        //使用屬性記錄閉包
        //completion = completion
        
        DispatchQueue.global().async {
            print("耗時操作")
            
            DispatchQueue.main.async {
                //回撥
                complete()
            }
        }
    }
    
    deinit {
        print("和dealloc一樣")
    }
    
}
//在ARC中,weak本質是一個觀察者模式,一旦物件釋放,則把物件置為nil
//在MRC中,是通過assign進行若引用的,如果物件釋放,assign的指標還是指向該內地地址,會造成野指標
__weak typeof(self) weakSelf = self;
//__unsafe_unretained相當於assign,
__unsafe_unretained typeof(self) weak1Self = self;

二、懶載入

注:懶載入只會在第一次呼叫時執行建立物件,後面如果物件被釋放了,則不會再次建立。而oc中會再次建立。

//1、懶載入的定義
    //懶載入的本質就是閉包
    lazy var person : Human = {
        print("懶載入的定義")
        return Human()
    }()
    
    //2、懶載入改寫為閉包形式
    let personFunc = { () -> Human in
        print("懶載入 --> 閉包")
        return Human()
    }
    
    lazy var personDemo : Human = self.personFunc()
    
    //3、懶載入的簡單寫法
    lazy var person2 : Human = Human()

三、UITableView的使用
import UIKit

class ViewController: UIViewController {

    // MARK:- 懶載入
    lazy var tableView : UITableView = {
        let tempTableView = UITableView()
        //可以做些事情
        
        return tempTableView;
    }()
    
    lazy var tableView2 : UITableView = UITableView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        setupUI()
    }
    
    
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        
    }


}
// MARK:- 設定UI
extension ViewController {
    ///設定UI
    func setupUI() {
        //1、設定tableView的屬性
        tableView.frame = CGRect.init(x: 0, y: 0, width: view.bounds.size.width, height: view.bounds.size.height)
        
        //2、設定資料來源
        tableView.delegate = self;
        tableView.dataSource = self;
        
        //3、加入到控制器view
        view.addSubview(tableView)
        
    }
}
// MARK:- UITableView的代理方法
//extension:類擴充套件只能擴充方法,不能擴充屬性
extension ViewController : UITableViewDelegate, UITableViewDataSource {
    /// UITableViewDelegate
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("點選了\(indexPath)")
    }
    
    /// UITableViewDataSource
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 20
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //1、建立cell
        let idenftify = "cellID"
        var cell = tableView.dequeueReusableCell(withIdentifier: idenftify)
        if cell == nil {
            cell = UITableViewCell.init(style: .default, reuseIdentifier: idenftify)
        }
        //2、賦值
        
        cell?.textLabel?.text = "測試\(indexPath.row)"
        
        //3、返回
        return cell!
    }
}


    //使用執行時獲取當前類所有屬性陣列
    class func propertyList() -> [String] {
        //1、獲取類的屬性列表
        var count : UInt32 = 0
        //返回屬性列表陣列
        let list = class_copyPropertyList(self, &count)
        
        print("屬性的數量:\(count)")
        
        //2、遍歷屬性列表
        for i in 0...Int(count) {
            
            //3、根據下標獲取屬性
            let pty = list?[i]
            
            //4、獲取屬性的名稱
            
            let cName = property_getName(pty!)
            
            //5、轉String
            let name = String.init(cString: cName!)
//            let name = String.init(describing: cName!)
            print(name)
        }
        
        //3、釋放c語言的物件
        free(list)
        
        return []
    }

//網路請求資料舉例
        let url = URL.init(string: "https://www.baidu.com")

        URLSession.shared.dataTask(with: url!) { (data, _, error) in
            if error != nil {
                print("網路載入失敗!")
            }
            
            guard let data = data else {
                print("網路請求失敗")
                return
            }
            
            let html = String.init(data: data, encoding: .utf8)
            print(html ?? "")
        }.resume()