获取声明为协议的 属性 时调用 didSet
didSet called when getting a property declared as a protocol
这是一些代码:
import UIKit
protocol ViewModelProtocol {
var price: String { get }
}
class ViewModel: ViewModelProtocol {
var price: String {
return "0"
}
}
class ViewController: UIViewController {
// If you change the type to ViewModel directly, no infinite loop
var viewModel: ViewModelProtocol? = nil {
didSet {
print("viewModel didSet called")
updateDisplay()
}
}
required init?(coder aDecoder: NSCoder) {
viewModel = ViewModel()
super.init(coder: aDecoder)
updateDisplay()
}
func updateDisplay() {
print("In updateDisplay()")
print("\(viewModel?.price)")
// if you access the viewModel like this, no infinite loop
// if let v = viewModel {
// print("\(v.price)")
// }
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
此代码将运行 无限循环。具体来说,它在 updateDisplay()
的 print("\(viewModel?.price)")
和 viewModel
的 didSet
之间反弹。
如果直接将viewModel
的类型改为ViewModel
(跳过协议),死循环就会消失。或者,如果在使用 updateDisplay()
之前将 viewModel
展开,无限循环也会消失。
这是在 Swift 2 中,虽然我还没有验证它是否在早期的 Swift 中具有相同的行为。另一个数据点,调用协议上的方法不会导致调用 didSet
.
您觉得这像是 Swift 错误吗?
这是一个非常令人印象深刻的案例。
您的 willSet 属性可能有问题,您的协议类型内部有一个 readonly 属性。我做了很多测试,很难找到解决方案。但是,如果你真的需要继续这样下去......我认为以下变化可以帮助你
protocol ViewModelProtocol {
var price: String { get set }
}
class ViewModel: ViewModelProtocol {
var price: String {
get {
return "0"
}
set {
return
}
}
//...
}
如您所说...只有在尝试通过 viewModel 对象直接访问价格属性时才会出现此问题
我只是把我的答案放在这里,因为它不适合评论字段的大小。
但是非常令人印象深刻...我将尝试找到最终解决方案。 :)
好像不止我一个didSet{}有这个问题。
有针对此问题的公开雷达:https://openradar.appspot.com/22574299
这是一些代码:
import UIKit
protocol ViewModelProtocol {
var price: String { get }
}
class ViewModel: ViewModelProtocol {
var price: String {
return "0"
}
}
class ViewController: UIViewController {
// If you change the type to ViewModel directly, no infinite loop
var viewModel: ViewModelProtocol? = nil {
didSet {
print("viewModel didSet called")
updateDisplay()
}
}
required init?(coder aDecoder: NSCoder) {
viewModel = ViewModel()
super.init(coder: aDecoder)
updateDisplay()
}
func updateDisplay() {
print("In updateDisplay()")
print("\(viewModel?.price)")
// if you access the viewModel like this, no infinite loop
// if let v = viewModel {
// print("\(v.price)")
// }
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
此代码将运行 无限循环。具体来说,它在 updateDisplay()
的 print("\(viewModel?.price)")
和 viewModel
的 didSet
之间反弹。
如果直接将viewModel
的类型改为ViewModel
(跳过协议),死循环就会消失。或者,如果在使用 updateDisplay()
之前将 viewModel
展开,无限循环也会消失。
这是在 Swift 2 中,虽然我还没有验证它是否在早期的 Swift 中具有相同的行为。另一个数据点,调用协议上的方法不会导致调用 didSet
.
您觉得这像是 Swift 错误吗?
这是一个非常令人印象深刻的案例。
您的 willSet 属性可能有问题,您的协议类型内部有一个 readonly 属性。我做了很多测试,很难找到解决方案。但是,如果你真的需要继续这样下去......我认为以下变化可以帮助你
protocol ViewModelProtocol {
var price: String { get set }
}
class ViewModel: ViewModelProtocol {
var price: String {
get {
return "0"
}
set {
return
}
}
//...
}
如您所说...只有在尝试通过 viewModel 对象直接访问价格属性时才会出现此问题
我只是把我的答案放在这里,因为它不适合评论字段的大小。
但是非常令人印象深刻...我将尝试找到最终解决方案。 :)
好像不止我一个didSet{}有这个问题。 有针对此问题的公开雷达:https://openradar.appspot.com/22574299