【问题标题】:Passing a value type as reference in Swift在 Swift 中传递值类型作为引用
【发布时间】:2017-09-10 00:25:33
【问题描述】:

我的模型在 Swift 3.0 中实现为 structs。其中几个structs 具有应该能够根据用户的操作修改模型的委托。

但是,当我将 struct 传递给 delegate 方法时,它会被复制。

你如何解决这个问题?您能否强制编译器将此struct 作为参考传递,或者唯一的选择是使用class

【问题讨论】:

  • 首先使用struct 的全部意义在于这是一种可取的行为。它保留了数据的不变性。 inout 可以做到这一点,但一般情况下不推荐。为什么复制是一个问题?
  • 那你如何从Controller修改模型呢?如果您有一个委托方法将模型作为第一个参数传递,就像在 Cocoa 中一样,委托(通常是控制器)如何修改 real 模型,而不是它的副本?
  • @cfischer 修改模型是控制器的工作,而不是委托。控制器应该询问委托如何修改模型,然后自己修改它。

标签: swift struct pass-by-reference value-type


【解决方案1】:

首先使用 struct 的全部意义在于这是一种可取的行为。它保留了数据的不变性。 inout 可以实现,但一般情况下不推荐。

protocol Delegate {
    func callback(_ oldValue: Int) -> Int
}

struct IncrementerDelegate: Delegate {
    let step: Int

    func callback(_ oldValue: Int) -> Int {
        return oldValue + step
    }
}

struct Model {
    var i = 0
}

class Controller {
    var model = Model()
    var delegate: Delegate

    init(delegate: Delegate) {
        self.delegate = delegate
    }

    // To be called externally, such as by a button
    func doSomething() {
        // Delegate determains new value, but it's still the
        // controller's job to perform the mutation.
        model.i = delegate.callback(model.i)
    }
}

let delegate = IncrementerDelegate(step: 5)
let controller = Controller(delegate: delegate)
print(controller.model.i)
controller.doSomething() // simulate button press
print(controller.model.i)








protocol CrappyDelegate {
    func callback(_ model: inout Model)
}

struct CrappyIncrementerDelegate: CrappyDelegate {
    let step: Int

    func callback(_ model: inout Model) {
        model.i = 9999999
        // Just hijacked the models value,
        // and the controller can't do anything about it.
    }
}

class VulnerableController {
    var model = Model()
    var delegate: CrappyDelegate

    init(delegate: CrappyDelegate) {
        self.delegate = delegate
    }

    // To be called externally, such as by a button
    func doSomething() {
        // Controller leaks entire model, and has no control over what happens to it
        delegate.callback(&model)
    }
}

let crappyDelegate = CrappyIncrementerDelegate(step: 5)
let vulnerableController = VulnerableController(delegate: crappyDelegate)
print(controller.model.i)
controller.doSomething() // simulate button press
print(controller.model.i) // model hijacked

【讨论】:

    【解决方案2】:

    如果你想通过引用传递,你通常应该使用class 而不是struct

    https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html 状态:

    您可以使用类和结构来定义自定义数据类型以 用作程序代码的构建块。

    但是,结构实例总是按值传递,而类 实例总是通过引用传递。这意味着他们是 适合不同类型的任务。当你考虑数据 项目所需的结构和功能,决定 每个数据结构应该定义为一个类还是一个 结构。

    【讨论】:

      【解决方案3】:

      structs 总是按值传递。使用struct 的全部意义在于让它表现为一个值类型。如果你需要委托(这通常意味着可变状态),你应该使用一个类。

      如果您真的需要,您可以使用inout 参数强制传递引用,但一般不建议这样做。您还可以使用box type 来模拟通过引用传递。但是,一般来说,如果你需要引用行为,你应该只使用一个类。

      【讨论】:

      • 一切,无论类型如何,在不使用inout时总是按值传递。
      • 嗯,从技术上讲是的,但是因为当你传递一个类引用时,你实际上是按值传递一个引用,它的作用几乎与按引用传递相同。
      猜你喜欢
      • 2018-10-31
      • 2021-08-13
      • 2015-03-22
      • 1970-01-01
      • 2023-04-09
      • 1970-01-01
      • 2017-10-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多