在 Swift 中传递值类型作为引用

Passing a value type as reference in Swift

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

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

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

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

如果您真的需要,您可以使用inout参数强制传递引用,但通常不推荐这样做。您还可以使用 box type 来模拟按引用传递。但是,一般来说,如果您需要参考行为,您应该只使用 class。

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

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

You can use both classes and structures to define custom data types to use as the building blocks of your program’s code.

However, structure instances are always passed by value, and class instances are always passed by reference. This means that they are suited to different kinds of tasks. As you consider the data constructs and functionality that you need for a project, decide whether each data construct should be defined as a class or as a structure.

首先使用 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