作者:Natasha the Robot,原文鏈接,原文日期:2015-12-23
譯者:lfb_CD;校對:numbbbbb;定稿:Cee
有一個常見的場景:一個 ViewController 控制多個 View ,并且想在 ViewController 中代理 View 的一些邏輯。
例如,你有一個 View,其中包含一個按鈕(比如在表單中的「注冊」),并且當用戶點擊這個注冊按鈕時,你希望代理其中的邏輯(比如注冊驗證和調用 API)。
你的代碼應該會是這樣的:
// 代理點擊的協議
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 關鍵字來約束協議,讓它只能被引用類型的數據(也就是類)使用:
// 協議只能被類使用!
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 框架打交道,所以你需要約束協議。
本文由 SwiftGG 翻譯組翻譯,已經獲得作者翻譯授權,最新文章請訪問 http://swift.gg。