1. 程式人生 > >swift閉包的迴圈引用

swift閉包的迴圈引用

例子是一個簡單通訊錄,列表介面點選新增聯絡人按鈕跳到新增介面,新增聯絡人後返回到列表介面ListVC,但是新增介面DetailVC沒有釋放

//
//  ListVC.swift
//  contract
//
//  Created by targetcloud on 2017/2/11.
//  Copyright © 2017年 targetcloud. All rights reserved.
//

import UIKit

class ListVC: UITableViewController {
    
    var personDataSource=[Person]()

    override func viewDidLoad() {
        super.viewDidLoad()

        loadData { (list) in
            print(list)
            self.personDataSource += list
            self.tableView.reloadData()
        }
    }
    
    private func loadData(completion:@escaping (_ listblock: [Person])->()) ->() {
        DispatchQueue.global().async {
            print("正在努力載入中...")
            Thread.sleep(forTimeInterval: 2)
            var arrayM :[Person] = [Person]()
            for i in 0..<20{
                let p = Person()
                p.name = "name - \(i)\(arc4random_uniform(100))"
                p.phone = "138"+String(format: "%08d", arc4random_uniform(100000000))
                p.title = "Boss"
                arrayM.append(p)
            }
            DispatchQueue.main.async {
                completion(arrayM)
            }
        }
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return personDataSource.count
    }

    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)
        cell.textLabel?.text = personDataSource[indexPath.row].name
        cell.detailTextLabel?.text = personDataSource[indexPath.row].phone
        return cell
    }
    
    
    @IBAction func newClick(_ sender: Any) {
        performSegue(withIdentifier: "new2detail", sender: nil)
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)
        performSegue(withIdentifier: "list2detail", sender: indexPath)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let vc = segue.destination as! DetailVC
        if let indexPath = sender as? IndexPath{
            vc.person = personDataSource[indexPath.row]
            vc.completionBlock = {
                self.tableView.reloadRows(at: [indexPath], with: .automatic)
            }
        }else{//new person
            vc.completionBlock = {
                guard let p = vc.person else {
                    return
                }
                self.personDataSource.insert(p, at: 0)
                self.tableView.reloadData()
            }
        }
    }
}

//
//  DetailVC.swift
//  contract
//
//  Created by targetcloud on 2017/2/11.
//  Copyright © 2017年 targetcloud. All rights reserved.
//

import UIKit

class DetailVC: UITableViewController {

    var person : Person?
    var completionBlock : (()->())?
    
    @IBOutlet weak var nameTF: UITextField!
    @IBOutlet weak var titleTF: UITextField!
    @IBOutlet weak var phoneTF: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        if person != nil{
            nameTF.text = person?.name
            phoneTF.text = person?.phone
            titleTF.text = person?.title
        }
    }

    @IBAction func saveClick(_ sender: Any) {
        if person == nil{//new person
            person = Person()
        }
        
        person?.name = nameTF.text
        person?.phone = phoneTF.text
        person?.title = titleTF.text
        completionBlock?()
        _=navigationController?.popViewController(animated: true)
    }

}

以下這段程式碼有問題
vc.completionBlock = {
                guard let p = vc.person else {
                    return
                }
                self.personDataSource.insert(p, at: 0)
                self.tableView.reloadData()
            }
正確寫法應該是
            vc.completionBlock = {[weak vc] in
                guard let p = vc?.person else {
                    return
                }
                self.personDataSource.insert(p, at: 0)
                self.tableView.reloadData()
            }

原因:vc對completionBlock有引用,閉包程式碼中又引用了vc,造成迴圈引用,所以使用前,要加[weak vc] in,用到vc的地方後面加?,即vc?