在 Swift 中传递值类型作为引用
Passing a value type as reference in Swift
我的模型在 Swift 3.0 中实现为 structs
。其中一些 structs
具有委托,应该能够根据用户的操作修改模型。
但是,当我将 struct
传递给 delegate
方法时,它会被复制。
你是怎么解决这个问题的?您能否强制编译器将此 struct
作为参考传递,或者唯一的选择是使用 class
?
struct
s 总是按值传递。使用 struct
的全部意义在于让它表现为值类型。如果你需要委托(这通常意味着可变状态),你应该使用 class。
如果您真的需要,您可以使用inout
参数强制传递引用,但通常不推荐这样做。您还可以使用 box type 来模拟按引用传递。但是,一般来说,如果您需要参考行为,您应该只使用 class。
如果你想通过引用传递,你通常应该使用 class
而不是 struct
。
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
我的模型在 Swift 3.0 中实现为 structs
。其中一些 structs
具有委托,应该能够根据用户的操作修改模型。
但是,当我将 struct
传递给 delegate
方法时,它会被复制。
你是怎么解决这个问题的?您能否强制编译器将此 struct
作为参考传递,或者唯一的选择是使用 class
?
struct
s 总是按值传递。使用 struct
的全部意义在于让它表现为值类型。如果你需要委托(这通常意味着可变状态),你应该使用 class。
如果您真的需要,您可以使用inout
参数强制传递引用,但通常不推荐这样做。您还可以使用 box type 来模拟按引用传递。但是,一般来说,如果您需要参考行为,您应该只使用 class。
如果你想通过引用传递,你通常应该使用 class
而不是 struct
。
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