iOS:如何用 Swift 實現弱代理

做者:Natasha the Robot,原文連接,原文日期:2015-12-23
譯者:lfb_CD;校對:numbbbbb;定稿:Ceeios

有一個常見的場景:一個 ViewController 控制多個 View ,而且想在 ViewController 中代理 View 的一些邏輯。git

例如,你有一個 View,其中包含一個按鈕(好比在表單中的「註冊」),而且當用戶點擊這個註冊按鈕時,你但願代理其中的邏輯(好比註冊驗證和調用 API)。github

你的代碼應該會是這樣的:swift

// 代理點擊的協議
protocol ButtonDelegate {
    func onButtonTap(sender: UIButton)
}

class ViewWithTextAndButton: UIView {

    // 保存代理,後面使用
    var delegate: ButtonDelegate?

    func onButtonTap(sender: UIButton) {
        // 按鈕被點擊的時候調用代理
        delegate?.onButtonTap(sender)
    }
}

class MyViewController: UIViewController, ButtonDelegate {

    let viewWithTextAndButton = ViewWithTextAndButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

    override func viewDidLoad() {
        super.viewDidLoad()

        // 給代理賦值
        viewWithTextAndButton.delegate = self
        view.addSubview(viewWithTextAndButton)
    }

    // MARK: ButtonDelegate
    // 實現代理邏輯
    func onButtonTap(sender: UIButton) {
        print("This button was clicked in the subview!")
    }

}

可是這裏還有一個很大的問題!由於 View 做爲 delegate 對 ViewController 是強引用,同時 ViewController 對 View 也是強引用,這就出現了循環引用。ViewController 引用着 View,而且 View 引用着 ViewController,二者的引用計數都不會變成 0,因此它們都不會被銷燬,從而形成內存泄露。框架

解決辦法就是讓其中一個對另外一個保持弱引用!在 Swift 中怎麼作呢?能夠添加 class 關鍵字來約束協議,讓它只能被引用類型的數據(也就是類)使用:ide

// 協議只能被類使用!
protocol ButtonDelegate: class {
    func onButtonTap(sender: UIButton)
}

接下來,咱們可使得咱們的代理被弱引用:翻譯

class ViewWithTextAndButton: UIView {

    // 注意,如今咱們可使用 weak 關鍵字!
    // 這個變量只能指向引用類型(UIViewController)
    // 而且是弱引用
    weak var delegate: ButtonDelegate?

    func onButtonTap(sender: UIButton) {
        delegate?.onButtonTap(sender)
    }
}

就是這樣!代理

這個例子很好地說明了爲何應該使用值類型——值類型能夠很好的避免循環引用。使用值類型時值會被拷貝,因此不會出現上述的內存泄露問題。不過呢,咱們又不得不和包含大量子類(UIView 和 UIViewController 的關係)的 Cocoa 框架打交道,因此你須要約束協議。code

本文由 SwiftGG 翻譯組翻譯,已經得到做者翻譯受權,最新文章請訪問 http://swift.gg內存

相關文章
相關標籤/搜索