I tried to write an "editor" class that could retain a reference to a property on a different object for later mutation. I first wrote the editor class to receive a closure for reading, and a closure for writing. This worked. I then tried to pass the parameter in question by (inout) reference, and then generate the getter / setter pair from that. This did not work. The Swift docs does say (paraphrasing) that Swift figures out when to copy, and when to not. I think I am up against unpredictability of that limitation, but thought I'd pose the question just the same.
Alternatively, is it possible to get a curried function for the individual getter and setter?
My code is:
class SomeModel : Printable {
var a:String
init(a:String) {
self.a = a
}
var description:String {
return "\(self.a)"
}
}
class Editor {
var getter:()-> String
var setter:(String)->()
init(getter:()-> String, setter:(String)->()) {
self.getter = getter
self.setter = setter
}
convenience init(inout bindTo:String) {
self.init(
getter:{ return bindTo },
setter: { v in bindTo = v })
}
func read() -> String {
return self.getter()
}
func write(value:String) {
self.setter(value)
}
}
func testBindTo() {
var readModel = SomeModel(a:"Did not capture by reference");
var bindForReading = Editor(bindTo: &readModel.a)
readModel.a = "captured by reference!"
println(bindForReading.read())
var writeModel = SomeModel(a:"Did not capture by reference");
var bindForWriting = Editor(bindTo: &writeModel.a)
bindForWriting.write("captured by reference")
println(writeModel)
}
testBindTo()
func testExplicitGetterSetter() {
var readModel = SomeModel(a:"Did not capture by reference");
var bindForReading = Editor(
getter: { readModel.a },
setter: { v in readModel.a = v })
readModel.a = "captured by reference!"
println(bindForReading.read())
var writeModel = SomeModel(a:"Did not capture by reference");
var bindForWriting = Editor(
getter: { writeModel.a },
setter: { v in writeModel.a = v })
bindForWriting.write("captured by reference")
println(writeModel)
}
testExplicitGetterSetter()
The results are:
Did not capture by reference
Did not capture by reference
captured by reference!
captured by reference
Thanks!
I don't think this is possible. And it shouldn't be possible, if you think about it, because it would be super unsafe.
Because closures can outlive the scope they were created in, captured variables must be stored with the block. But in order to be able to assign to the captured variable and share the state of that variable between the (one or more) block(s) that captured it and the original scope, the blocks cannot just capture the value of the variable (which would create independent copies of the variable), but capture a kind of "reference" to a shared copy. This means that assignable variables that are captured by blocks must be stored specially. In Objective-C, this is declared with __block
. In Swift, this __block
behavior is implicit.
However, in order for the block to modify an inout
variable (potentially at a later time) as it is seen in the function caller's scope, that would mean that the passed variable in the caller's scope would also need to be stored in a way that can outlive the stack frame. But the caller function doesn't know this. All it knows from the type of the called function is that one of its parameters is inout
; it doesn't know that the function plans to capture that inout
variable in a block. So it doesn't know to prepare this __block
storage for this passed variable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With