做者: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。內存