Reputation: 9467
This is what I'd like to do, but quantity property isn't available.
struct MyStruct {
var quantity: Int
let valueFn: () -> (Int)
}
var s1 = MyStruct(quantity: 2) { () -> (Int) in
return quantity * 2 // error: can't use `quantity`
}
It doesn't work with classes either:
class MyClass {
var quantity: Int
let valueFn: () -> (Int)
init(valueFn: () -> (Int)) {
self.valueFn = valueFn
}
}
var c1 = MyClass { () -> (Int) in
return quantity // error: can't use quantity or self
}
This one works, but I have to explicitly pass in the struct or struct property. My question is this: Can I do this without having a third property to wrap and pass in the reference? Is there a more Swifty approach?
struct MyOtherStruct {
var quantity: Int
private let valueFn: (Int) -> (Int)
var value: Int {
return valueFn(quantity)
}
}
var s3 = MyOtherStruct(quantity: 2) { (quantity) -> (Int) in
return quantity * 2
}
s3.value // -> 4
Upvotes: 2
Views: 703
Reputation: 73186
I've fibbled with this the last 20 minutes now, and I don't believe this is possible to achieve in the initialization of of your MyStruct
instances; since at the call to the initializer (which you do with the trailing closure above), the instance does not yet exist. I will show below, however, a workaround for classes; one much like your third approach above, and one with more versatile usage (but horribly worse class implementation), making use of subclassing NSObject
.
Anyway, possibly the above answers your question (as "no" for structs, and "possible, but overly complicated" for classes) with regard of use directly with the initializer in a versatile manner.
Below follows
A closure "wrapping" method much your third solution: wrapping a lazy closure over the closure you send at initialization. The drawback with this method is that you can't choose, for different class instances, which class properties you would like to use in the closure, as the wrapper (calling the mutable initialized closure) is already set at compile time.
Subclassing NSObject
: a method that is probably overly complicated for any practical use (and also quite non-swifty), that however give you more versatile control over which class properties to use in the closure. By subclassing NSObject
you get access to the method .valueForKey
to apply to self
. Added for the technical discussion/curiosity.
Method 1
class MyClass {
var quantity: Int
private let closure: (Int) -> (Int)
init(quantity: Int, closure: (Int) -> (Int)) {
self.quantity = quantity
self.closure = closure
}
lazy var valueFn : () -> Int = {
[unowned self] () -> Int in
return self.closure(self.quantity)
}
}
/* Example usage */
var c1 = MyClass(quantity: 2) { (a) -> (Int) in
return a * 2
}
c1.valueFn() // 4
c1.quantity = 4
c1.valueFn() // 8
Method 2
Class setup (...):
class MyClass : NSObject {
var quantity: Int = 0
var anotherQuantity: Int = 0
private var keyExists : Bool = true
private let key : String
private let foo: (Int) -> Int
init(operateClosureOn: (propertyWithKey: String, withInitialValue: Int),
closure: (Int) -> Int) {
key = operateClosureOn.propertyWithKey
foo = closure
super.init()
let val = operateClosureOn.withInitialValue
if let _ = (Mirror(reflecting: self).children.filter{ $0.label == key }).first {
self.setValue(val, forKey: key)
}
else { keyExists = false }
}
lazy var valueFn: () -> Int = {
[unowned self] () -> Int in
if !self.keyExists {
return 0
}
guard let a = self.valueForKey(self.key) as? Int else {
print("Unexpected: property for key '\(self.key)' is not if type 'Int'.")
return 0
}
return self.foo(a)
}
}
Example usage:
/* Example usage */
var c2 = MyClass(operateClosureOn: ("quantity", 2)) {
(val) -> Int in
return 2 * val
}
c2.valueFn() // 4
c2.quantity = 4
c2.valueFn() // 8
var c3 = MyClass(operateClosureOn: ("anotherQuantity", 20)) {
(val) -> Int in
return val / 2
}
c3.valueFn() // 10
c3.anotherQuantity = 40
c3.valueFn() // 20
var c4 = MyClass(operateClosureOn: ("aTypo", 20)) {
(val) -> Int in
return val / 2
}
c4.valueFn() // 0, OK, at least no runtime exception with this non-safe non-swifty solution :0)
Upvotes: 1