【问题标题】:didSet called when getting a property declared as a protocol获取声明为协议的属性时调用 didSet
【发布时间】:2015-09-21 22:39:41
【问题描述】:

这里有一些代码:

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.
    }
}

此代码将在无限循环中运行。具体来说,它在updateDisplay() 中的print("\(viewModel?.price)")viewModel 中的didSet 之间反弹。

如果直接将viewModel的类型改为ViewModel(跳过协议),死循环就消失了。或者,如果您在使用之前将viewModel 解包到updateDisplay() 中,则无限循环也会消失。

这是在 Swift 2 中,虽然我还没有验证它在早期的 Swift 中是否具有相同的行为。另一个数据点,在协议上调用方法不会导致调用didSet

在你看来,这看起来像一个 Swift 错误吗?

【问题讨论】:

    标签: swift protocols swift2 optional


    【解决方案1】:

    看来,didSet{} 不仅有我的问题。 这个问题有公开的雷达:https://openradar.appspot.com/22574299

    【讨论】:

      【解决方案2】:

      这是一个非常令人印象深刻的案例。

      您的 willSet 属性可能存在问题,因为您的协议类型内部具有只读属性。我做了很多测试,很难找到解决方案。但是,如果你真的需要以这种方式继续前进......我认为以下更改可以帮助你

      protocol ViewModelProtocol {
          var price: String { get set }
      }
      
      class ViewModel: ViewModelProtocol {
          var price: String  {
              get {
                  return "$420"
              }
      
              set {
                  return 
              }
          }
          //...
      }
      

      正如你所说...只有在尝试通过 viewModel 对象直接访问 price 属性时才会出现问题

      我只是把我的答案放在这里,因为它不适合评论字段的大小。

      但是非常令人印象深刻...我会尝试找到最终解决方案。 :)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-11-21
        • 1970-01-01
        • 2018-12-20
        • 1970-01-01
        • 1970-01-01
        • 2023-04-06
        • 1970-01-01
        相关资源
        最近更新 更多