这里有一些代码:
import UIKit
protocol ViewModelProtocol {
var price: String { get }
}
class ViewModel: ViewModelProtocol {
var price: String {
return "$420"
}
}
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.
}
}
此代码将以无限循环运行。具体来说,它会在print("\(viewModel?.price)")
中的updateDisplay()
和didSet
的{{1}}之间反弹。
如果您直接将viewModel
的类型更改为viewModel
(跳过协议),则无限循环将消失。或者,如果您在使用ViewModel
之前打开viewModel
,则无限循环也会消失。
这是在Swift 2中,虽然我还没有验证它是否在早期的Swift中具有相同的行为。另一个数据点,调用协议上的方法并不会导致调用updateDisplay()
。
这看起来像是一个Swift错误吗?
答案 0 :(得分:2)
这是一个令人印象深刻的案例。
你的willSet属性可能存在问题,你的协议类型里面有一个readonly属性。我做了很多测试,很难找到解决方案。但是,如果你真的需要继续这样做......我认为以下改变可以帮助你
protocol ViewModelProtocol {
var price: String { get set }
}
class ViewModel: ViewModelProtocol {
var price: String {
get {
return "$420"
}
set {
return
}
}
//...
}
正如您所说......只有在尝试通过viewModel对象直接访问price属性时才会出现问题
我只是在这里提出答案,因为它不适合评论字段大小。
但令人印象深刻......我会尽力找到最终解决方案。 :)
答案 1 :(得分:2)
似乎不仅我在didSet {}中遇到了这个问题。 这个问题有一个开放的雷达:https://openradar.appspot.com/22574299