Reputation: 24912
I have my model implemented as structs
in Swift 3.0. Several of these structs
have delegates that should be able to modify the model depending on the user's actions.
However, when I pass the struct
to the delegate
method, it gets copied.
How do you solve this? Can you force the compiler to pass this struct
as a reference, or the only option is to use a class
?
Upvotes: 4
Views: 3718
Reputation: 63271
The whole point of using struct in the first place is that this is desirable behavior. It preserves the immutability of the data. inout
can achieve this, but it's not recommended in the general case.
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
Upvotes: 2
Reputation: 6705
If you want to pass by reference, you should generally use a class
not a 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.
Upvotes: 0
Reputation: 10532
struct
s are always passed by value. The whole point of using a struct
is to have it behave as a value type. If you need delegation (which usually implies mutable state), you should be using a class.
If you really really need to, you could force pass-by-reference by using an inout
parameter, but that is not recommended in general. You could also use a box type to simulate passing by reference. But, in general, you should just use a class if you need reference behavior.
Upvotes: 4