I am overloading (or maybe implementing in this case) the += operator for a class (not a struct!). The operation modifies the state of the left-hand-side instance. I noticed that I can declare the left-hand-side element with let without any errors (and since it is an instance of a class, it's internal state changes with the operation). This of course is undesired, and should result in a compile-time-error. Is there a way to declare the overloaded operator as mutating to the left-hand-side element?
class MyClass {
static func +=(lhs: MyClass, rhs: MyClass) {
lhs.fu(rhs) // fu() changes internal state of lhs
}
}
let a = MyClass()
let b = MyClass()
a += b // this is legal but shouldn't be, since instance 'a' will
// have a different internal state after the concatenation
The let constant in this case is the reference a to the MyClass object that it points to. It prevents you from being able to do this:
let a = MyClass()
a = MyClass() //redefinition not allowed
It does not guarantee anything about the constancy of the members of that object however. Classes/objects exist to model constantly changing data, marking methods as mutating would be a bit tedious, because ultimately that's what they're supposed to do, in general.
You should be using structs in cases where you want controlled mutation.
Related
Is there any syntax can make this work? I need a property can determine its type in the compile time.
protocol P {}
struct A: P {
var onlyAHas: String
}
struct B: P {
var onlyBHas: String
}
var ins1: any P = A()
var ins2: any P = B()
ins1.onlyAHas = "a only"
ins2.onlyBHas = "b only"
Before getting to the solution, let's break down what any means, and while we're at it, we'll include some as well:
When you write:
var ins1: any P = A()
You are telling the compiler that you want to use ins1 as P. It's the protocol oriented equivalent of this OOP code:
class Base {
var baseProperty: String? = nil
}
class Concrete: Base {
var concreteProperty: String? = nil
}
let obj: Base = Concrete();
obj.baseProperty = "Some value" // <-- This is fine
obj.concreteProperty = "Some value" // <-- This is an error
This code tells the compiler that obj is a Base. You can assign it from a Concrete, but because that's a subclass of Base, but obj is still known locally as a Base not as a Concrete, so it can't access the properties of Concrete that weren't inherited from Base.
It's the same in your example. ins1 is known locally as a P not as an A, and P doesn't have an onlyAHas property.
You'd get similar behavior with some instead of any. There are a few differences between the two, but let's just talk about the main one:
some tells the compiler that it will be a type that it can resolve to one specific concrete type, but that it should enforce the abstraction to the protocol in source code. This allows it to generate more efficient code internally, because knowing the concrete type allows the compiler to call the concrete's implementation directly instead of going through its protocol witness table, which is the protocol-oriented analog of a "vtable" in OOP, so the effect is like in OOP when the compiler devirtualizes a method call because despite the syntax, it knows the actual concrete type. This avoids the runtime overhead of dynamic dispatch while still letting you use the abstraction of the existential type... well it's more like it requires you to use the abstraction of the existential type than lets you, because from a source code point of view, the abstraction is enforced.
any also enforces the abstraction, but it goes the other way in terms of the kind of optimizations the compiler can do. It says that the compiler must go through the protocol witness table, because, as the keyword suggests, its value could be any concrete type that conforms to the protocol, even if the compiler could determine that it's actually just one specific type locally. It also allows relaxation of some rules regarding using the protocol as a type when it has Self and associatedtype constraints.
But either way, you are telling the compiler that you want to use ins1 as a P and not as an A.
The solutions
There are a few solutions, actually:
Downcasting
The first is to downcast to the concrete type, as was suggested in comments by Joakim Danielson:
if var ins1 = ins1 as? A {
ins1.onlyAHas = "a only"
}
Downcasting is a code smell, but sometimes is actually the clearest or simplest solution. As long as it's contained locally, and doesn't become a wide-spread practice for using instances of type, P, it might be fine.
However, that example does have one problem: A is a value type, so the ins1 whose onlyAHas property is being set is a copy of the original ins1 you explicitly created. Having the same name confuses it slightly. If you only need the change to be in effect in the body of the if, that works just fine. If you need it to persist outside, you'd have to assign back to the original. Using the same name prevents that, so you'd need to use different names.
Execute concrete-specific code only at initialization
This only applies if the concrete type just configures some things for the protocol up-front, and thereafter protocol-only code can be used:
var ins1: any P = A(onlyAHas: "a only")
// From here on code can only do stuff with `ins1` that is defined in `P`
Or your could delegate the initialization to a function that internally knows the concrete type, but returns any P.
func makeA(_ s: String) -> any P
{
var a = A()
a.onlyAHas = s
return a
}
var ins1 = makeA("a only");
// From here on code can only do stuff with `ins1` that is defined in `P`
Declare protocol methods/computed properties that do the work.
This is the usual way to use protocols. Declaring a method in the protocol is similar to declaring a method in a base class. Implementing the method in a conforming concrete type is like overriding the method in a subclass. If you don't also provide a default implementation in a protocol extension, the protocol will enforce that conforming types implement the protocol - which is a big advantage over the OOP approach.
protocol P {
mutating func setString(_ s: String)
}
struct A: P
{
var onlyAHas: String
mutating func setString(_ s: String) {
onlyAHas = s
}
}
struct B: P
{
var onlyBHas: String
mutating func setString(_ s: String) {
onlyBHas = s
}
}
var ins1: any P = A()
var ins2: any P = B()
ins1.setString("a only") // <- Calls A's setString
ins2.setString("b only") // <- Calls B's setString
I'm doing this with a setString method, but you could certainly use a computed variable in the protocol to do the same thing, and that would be more "Swifty." I didn't do that just to emphasize the more general idea of putting functionality in the protocol, and not get hung up on the fact that the functionality in question happens to be setting a property.
If you don't need all conforming types to be able to set a String, one solution is to provide a do-nothing default implmentation in an extension on P:
protocol P {
mutating func setString(_ s: String)
}
extension P
{
mutating func setString(_ s: String) { /* do nothing */ }
}
// Same A and B definitions go here
struct C: P { }
var ins3: any P = C();
ins1.setString("a only") // <- Calls A's setString
ins2.setString("b only") // <- Calls B's setString
ins3.setString("c only") // <- Calls setString from extension of P
Most often though, setting/getting some concrete property is an implementation detail of doing some task that varies with the concrete type. So instead, you'd declare a method in the protocol to do that task:
protocol P
{
mutating func frobnicate()
}
struct A
{
var onlyAHas: String
mutating func frobnicate()
{
// Do some stuff
onlyAHas = "a only"
// Do some other stuff that uses onlyAHas
}
}
B would be defined similarly doing whatever is specific to it. If the stuff in comments is common code, you could break it down into prologue, main action, and epilogue.
protocol P
{
mutating func prepareToFrobnicate()
mutating func actuallyFrobnicate() -> String
mutating func finishFrobnication(result: String)
}
extension P
{
/*
This method isn't in protocol, so this exact method will be called;
however, it calls methods that *are* in the protocol, we provide
default implementations, so if conforming types, don't implement them,
the versions in this extension are called, but if they do implement
them, their versions will be called.
*/
mutating func frobnicate()
{
prepareToFrobnicate()
finishFrobnication(result: actuallyFrobnicate());
}
mutating func prepareToFrobnicate() {
// do stuff general stuff to prepare to frobnicate
}
mutating func actuallyFrobnicate() -> String {
return "" // just some default value
}
mutating func finishFrobnication(result: String) {
// define some default behavior
}
}
struct A
{
var onlyAHas: String
mutating func actuallyFrobnicate() -> String
{
// Maybe do some A-specific stuff
onlyAHas = "a only"
// Do some more A-specific stuff
return onlyAHas
}
}
struct B
{
var onlyBHas: String
mutating func actuallyFrobnicate() -> String {
"b only"
}
mutating func finishFrobnication(result: String)
{
// Maybe do some B-specific stuff
onlyBHas = result
// Do some more B-specific stuff
}
}
var ins1: any P = A()
var ins2: any P = B()
ins1.frobnicate();
ins2.frobnicate();
In this example, the frobnicate in the protocol extension is called, because it's defined only in the protocol extension.
For ins1, frobnicate then calls the extension's prepareToFrobnicate, because even though it's declared directly in the protocol, A doesn't implement that and a default implementation is provided in the extension.
Then it calls A's actuallyFrobnicate because it's defined directly in the protocol, and A does implement it, so the default implementation isn't used. As a result the onlyAHas property is set.
Then it passes the result from A's actuallyFrobnicate to the finishFrobnication in the extension, because it's defined directly in the protocol, but A doesn't implement it, and the extension provides a default implementation.
For ins2, frobnicate still calls the default prepareToFrobnicate, and then call's B's implementation of actuallyFrobnicate, but B's implementation doesn't set its onlyBHas property there. Instead, it just returns a string, which frobnicate passes to finishFrobnication, which calls B's implementation, because unlike A, B provides its own implementation, and that's where B sets it.
Using this approach, you can simultaneously standardize the general algorithm of a task like frobnicate, while allowing for dramatically different implementation behavior. Of course, in this case, both A and B just set a property in their respective concrete types, but they do it at different phases of the algorithm, and you can imagine adding other code, so that the two effects really would be very different.
The point of this approach is that when we call inst1.frobnicate(), it doesn't know or care about exactly what inst1 is doing internally do accomplish it. The fact that it internally sets the onlyAHas property in the concrete type is an implementation detail the calling code doesn't need to be concerned with.
Just use the concrete type
In your code example, you are creating and using ins1, and ins2 in the same context. So they could just as easily be defined like this:
var ins1 = A()
var ins2 = B()
ins1.onlyAHas = "a only" // <- This is fine because ins1 is an A
ins2.onlyBHas = "b only" // <- This is fine because ins2 is a B
If you have some function, munge that you want to do on both A and B, you can define it terms of the protocol.
func munge(_ p: any P)
{
// In here you can only use `p` as defined by the protocol, `P`
}
If munge needs to do things that depend on concrete-specific properties or methods, you can use one of the previously described approaches...
OR...
If you know for sure that you only will ever have a small number of concrete types conforming to P, which admittedly is sometimes impossible to really know, but occasionally you do, then you can just write specialized overloaded versions of munge for each concrete type:
func munge(_ a: A) {
// Do `A`-specific stuff with `a`
}
func munge(_ b: B) {
// Do `B`-specific stuff with `b`
}
This kind of regresses to older solutions to problems like this. When I say it's an old solution, I'm referring to the fact that even back when the C++ compiler was just a preprocessor that converted C++ source code to C source code which would then be compiled, didn't have templates, and standardization wasn't even on the horizon, it would let you overload functions. You can do that with Swift too, and it's a perfectly valid solution. Sometimes it's even the best solution. More often it leads to code duplication, but it's in your toolbox to use when it's appropriate.
Why must I insert mutating before implementing a method on a struct when adopting protocols?
Why don't I need mutating when I do the same thing in a class?
The mutating keyword is only required if you are changing any state contained within the struct. Since Swift structs are immutable objects, calling a mutating function actually returns a new struct in-place (much like passing an inout parameter to a function). The mutating keyword lets callers know that the method is going to make the value change. The best way to conceptualize this is to think of your struct the same as you would a number: if you perform the operation 4 + 1, 4 doesn’t become 5, you’ve just gotten a new value after performing the operation. Mutating functions operate on the same principle. You cannot call mutating functions on constants (e.g. let someStruct = SomeStruct()), because that would be the same thing as trying to assign the constant to a new value. Because of this behavior mutating functions can only be performed on variables (e.g var someStruct = SomeStruct()).
Being the value type structs are immutable. Meaning other variables can not change the values for instance of structure at any given point.
The mutating word is required for changing the values of self variables inside structure's function ONLY.
For. e.g
struct MyStruct {
var abc: String = "initila value"
func changeValue() {
abc = "some other value". //Compile time error: Cannot assign to property: 'self' is immutable. Mark method 'mutating' to make 'self' mutable.
}
}
Here as we are trying to change value of variable abc inside function declared in struct itself, we get the compile time error.
So here we need to make function mutating to make change value inside structure. Hence the correct code will be:
struct MyStruct {
var abc: String = "initila value"
mutating func changeValue() {
abc = "some other value"
}
}
EDIT:
When declaring protocol, it can be declared commonly for reference and value types, so these kind of protocols itself declares the functions as mutating so that it can be adopted by both classes and structures.
Since being reference type the mutating keyword is removed (or we can say not required) in the classes, but for structures being value types the mutating keyword is required.
From the docs:
If you define a protocol instance method requirement that is intended to mutate instances of any type that adopts the protocol, mark the method with the mutating keyword as part of the protocol’s definition. This enables structures and enumerations to adopt the protocol and satisfy that method requirement.
If you mark a protocol instance method requirement as mutating, you don’t need to write the mutating keyword when writing an implementation of that method for a class. The mutating keyword is only used by structures and enumerations.
Reference
I hope this will clear your doubt.
Classes are reference types. What that means is that a variable typed to a class:
let someObject = SomeClass()
just contains a pointer to that class's memory, under the hood. The class's contents and data can change without changing the original pointer, because it's just a reference.
Structs, on the other hand, are value types. If you have a variable containing something that is a struct type:
var someStruct = SomeStruct()
the variable itself is actually containing all the struct's data. Changing the struct's internal state actually involves reassigning the variable—so in the example above, something like someStruct.foo = "bar" would actually cause the someStruct variable to be reassigned, as if you'd typed:
someStruct = SomeStruct(foo: "bar", otherStuff: someStruct.otherStuff) // or something of this nature
This is also why you have to declare structs using var if you plan to change anything in them, whereas this isn't so with classes.
As for protocols, they can represent either struct or class types, so if you are dealing with a protocol existential, you can't do operations on it that assume it's a class (unless the protocol is constrained as such).
The Mutating Keyword : In order to modify the properties of a value type, you have to use the mutating keyword in the instance method. With this keyword, your method can then have the ability to mutate the values of the properties and write it back to the original structure when the method implementation ends.
Class is not support In swift, classes are reference type whereas structures and enumerations are value types. The properties of value types cannot be modified within its instance methods by default. In order to modify the properties of a value type, you have to use the mutating keyword in the instance method.
I have a custom operator defined globally like so:
func ==(lhs: Item!, rhs: Item!)->Bool {
return lhs?.dateCreated == rhs?.dateCreated
}
And if I execute this code:
let i1 = Item()
let i2 = Item()
let date = Date()
i1.dateCreated = date
i2.dateCreated = date
let areEqual = i1 == i2
areEqual is false. In this case I know for sure that my custom operator is not firing. However, if I add this code into the playground:
//same function
func ==(lhs: Item!, rhs: item!)->Bool {
return lhs?.dateCreated == rhs?.dateCreated
}
//same code
let i1 = Item()
let i2 = Item()
let date = Date()
i1.dateCreated = date
i2.dateCreated = date
let areEqual = i1 == i2
areEqual is true -- I'm assuming my custom operator is fired in this case.
I have no other custom operators defined that would cause a conflict in the non-playground case, and the Item class is the same in both cases, so why is my custom operator not being called outside the playground?
The Item class inherits from the Object class provided by Realm, which eventually inherits from NSObject. I also noticed that if I define non-optional inputs for the overload, when the inputs are optionals it's not fired.
There are two main problems with what you're trying to do here.
1. Overload resolution favours supertypes over optional promotion
You've declared your == overload for Item! parameters rather than Item parameters. By doing so, the type checker is weighing more in favour of statically dispatching to NSObject's overload for ==, as it appears that the type checker favours subclass to superclass conversions over optional promotion (I haven't been able to find a source to confirm this though).
Usually, you shouldn't need to define your own overload to handle optionals. By conforming a given type to Equatable, you'll automatically get an == overload which handles equality checking between optional instances of that type.
A simpler example that demonstrates the favouring of a superclass overload over an optional subclass overload would be:
// custom operator just for testing.
infix operator <===>
class Foo {}
class Bar : Foo {}
func <===>(lhs: Foo, rhs: Foo) {
print("Foo's overload")
}
func <===>(lhs: Bar?, rhs: Bar?) {
print("Bar's overload")
}
let b = Bar()
b <===> b // Foo's overload
If the Bar? overload is changed to Bar – that overload will be called instead.
Therefore you should change your overload to take Item parameters instead. You'll now be able to use that overload to compare two Item instances for equality. However, this won't fully solve your problem due to the next issue.
2. Subclasses can't directly re-implement protocol requirements
Item doesn't directly conform to Equatable. Instead, it inherits from NSObject, which already conforms to Equatable. Its implementation of == just forwards onto isEqual(_:) – which by default compares memory addresses (i.e checks to see if the two instances are the exact same instance).
What this means is that if you overload == for Item, that overload is not able to be dynamically dispatched to. This is because Item doesn't get its own protocol witness table for conformance to Equatable – it instead relies on NSObject's PWT, which will dispatch to its == overload, simply invoking isEqual(_:).
(Protocol witness tables are the mechanism used in order to achieve dynamic dispatch with protocols – see this WWDC talk on them for more info.)
This will therefore prevent your overload from being called in generic contexts, including the aforementioned free == overload for optionals – explaining why it doesn't work when you attempt to compare Item? instances.
This behaviour can be seen in the following example:
class Foo : Equatable {}
class Bar : Foo {}
func ==(lhs: Foo, rhs: Foo) -> Bool { // gets added to Foo's protocol witness table.
print("Foo's overload") // for conformance to Equatable.
return true
}
func ==(lhs: Bar, rhs: Bar) -> Bool { // Bar doesn't have a PWT for conformance to
print("Bar's overload") // Equatable (as Foo already has), so cannot
return true // dynamically dispatch to this overload.
}
func areEqual<T : Equatable>(lhs: T, rhs: T) -> Bool {
return lhs == rhs // dynamically dispatched via the protocol witness table.
}
let b = Bar()
areEqual(lhs: b, rhs: b) // Foo's overload
So, even if you were to change your overload such that it takes an Item input, if == was ever called from a generic context on an Item instance, your overload won't get called. NSObject's overload will.
This behaviour is somewhat non-obvious, and has been filed as a bug – SR-1729. The reasoning behind it, as explained by Jordan Rose is:
[...] The subclass does not get to provide new members to satisfy the conformance. This is important because a protocol can be added to a base class in one module and a subclass created in another module.
Which makes sense, as the module in which the subclass resides would have to be recompiled in order to allow it to satisfy the conformance – which would likely result in problematic behaviour.
It's worth noting however that this limitation is only really problematic with operator requirements, as other protocol requirements can usually be overridden by subclasses. In such cases, the overriding implementations are added to the subclass' vtable, allowing for dynamic dispatch to take place as expected. However, it's currently not possible to achieve this with operators without the use of a helper method (such as isEqual(_:)).
The Solution
The solution therefore is to override NSObject's isEqual(_:) method and hash property rather than overloading == (see this Q&A for how to go about this). This will ensure that your equality implementation will always be called, regardless of the context – as your override will be added to the class' vtable, allowing for dynamic dispatch.
The reasoning behind overriding hash as well as isEqual(_:) is that you need to maintain the promise that if two objects compare equal, their hashes must be the same. All sorts of weirdness can occur otherwise, if an Item is ever hashed.
Obviously, the solution for non-NSObject derived classes would be to define your own isEqual(_:) method, and have subclasses override it (and then just have the == overload chain to it).
I have a custom operator defined globally like so:
func ==(lhs: Item!, rhs: Item!)->Bool {
return lhs?.dateCreated == rhs?.dateCreated
}
And if I execute this code:
let i1 = Item()
let i2 = Item()
let date = Date()
i1.dateCreated = date
i2.dateCreated = date
let areEqual = i1 == i2
areEqual is false. In this case I know for sure that my custom operator is not firing. However, if I add this code into the playground:
//same function
func ==(lhs: Item!, rhs: item!)->Bool {
return lhs?.dateCreated == rhs?.dateCreated
}
//same code
let i1 = Item()
let i2 = Item()
let date = Date()
i1.dateCreated = date
i2.dateCreated = date
let areEqual = i1 == i2
areEqual is true -- I'm assuming my custom operator is fired in this case.
I have no other custom operators defined that would cause a conflict in the non-playground case, and the Item class is the same in both cases, so why is my custom operator not being called outside the playground?
The Item class inherits from the Object class provided by Realm, which eventually inherits from NSObject. I also noticed that if I define non-optional inputs for the overload, when the inputs are optionals it's not fired.
There are two main problems with what you're trying to do here.
1. Overload resolution favours supertypes over optional promotion
You've declared your == overload for Item! parameters rather than Item parameters. By doing so, the type checker is weighing more in favour of statically dispatching to NSObject's overload for ==, as it appears that the type checker favours subclass to superclass conversions over optional promotion (I haven't been able to find a source to confirm this though).
Usually, you shouldn't need to define your own overload to handle optionals. By conforming a given type to Equatable, you'll automatically get an == overload which handles equality checking between optional instances of that type.
A simpler example that demonstrates the favouring of a superclass overload over an optional subclass overload would be:
// custom operator just for testing.
infix operator <===>
class Foo {}
class Bar : Foo {}
func <===>(lhs: Foo, rhs: Foo) {
print("Foo's overload")
}
func <===>(lhs: Bar?, rhs: Bar?) {
print("Bar's overload")
}
let b = Bar()
b <===> b // Foo's overload
If the Bar? overload is changed to Bar – that overload will be called instead.
Therefore you should change your overload to take Item parameters instead. You'll now be able to use that overload to compare two Item instances for equality. However, this won't fully solve your problem due to the next issue.
2. Subclasses can't directly re-implement protocol requirements
Item doesn't directly conform to Equatable. Instead, it inherits from NSObject, which already conforms to Equatable. Its implementation of == just forwards onto isEqual(_:) – which by default compares memory addresses (i.e checks to see if the two instances are the exact same instance).
What this means is that if you overload == for Item, that overload is not able to be dynamically dispatched to. This is because Item doesn't get its own protocol witness table for conformance to Equatable – it instead relies on NSObject's PWT, which will dispatch to its == overload, simply invoking isEqual(_:).
(Protocol witness tables are the mechanism used in order to achieve dynamic dispatch with protocols – see this WWDC talk on them for more info.)
This will therefore prevent your overload from being called in generic contexts, including the aforementioned free == overload for optionals – explaining why it doesn't work when you attempt to compare Item? instances.
This behaviour can be seen in the following example:
class Foo : Equatable {}
class Bar : Foo {}
func ==(lhs: Foo, rhs: Foo) -> Bool { // gets added to Foo's protocol witness table.
print("Foo's overload") // for conformance to Equatable.
return true
}
func ==(lhs: Bar, rhs: Bar) -> Bool { // Bar doesn't have a PWT for conformance to
print("Bar's overload") // Equatable (as Foo already has), so cannot
return true // dynamically dispatch to this overload.
}
func areEqual<T : Equatable>(lhs: T, rhs: T) -> Bool {
return lhs == rhs // dynamically dispatched via the protocol witness table.
}
let b = Bar()
areEqual(lhs: b, rhs: b) // Foo's overload
So, even if you were to change your overload such that it takes an Item input, if == was ever called from a generic context on an Item instance, your overload won't get called. NSObject's overload will.
This behaviour is somewhat non-obvious, and has been filed as a bug – SR-1729. The reasoning behind it, as explained by Jordan Rose is:
[...] The subclass does not get to provide new members to satisfy the conformance. This is important because a protocol can be added to a base class in one module and a subclass created in another module.
Which makes sense, as the module in which the subclass resides would have to be recompiled in order to allow it to satisfy the conformance – which would likely result in problematic behaviour.
It's worth noting however that this limitation is only really problematic with operator requirements, as other protocol requirements can usually be overridden by subclasses. In such cases, the overriding implementations are added to the subclass' vtable, allowing for dynamic dispatch to take place as expected. However, it's currently not possible to achieve this with operators without the use of a helper method (such as isEqual(_:)).
The Solution
The solution therefore is to override NSObject's isEqual(_:) method and hash property rather than overloading == (see this Q&A for how to go about this). This will ensure that your equality implementation will always be called, regardless of the context – as your override will be added to the class' vtable, allowing for dynamic dispatch.
The reasoning behind overriding hash as well as isEqual(_:) is that you need to maintain the promise that if two objects compare equal, their hashes must be the same. All sorts of weirdness can occur otherwise, if an Item is ever hashed.
Obviously, the solution for non-NSObject derived classes would be to define your own isEqual(_:) method, and have subclasses override it (and then just have the == overload chain to it).
Question: For the case of ()-return closures, is there any way to access a variable that ARC lets live only due to a single strong reference from a closure closing over it? In the example below: accessing bb in the closure.
Below follows an example to show what I mean.
In the Language Reference - Expressions, it reads
Capture Lists
By default, a closure expression captures constants and variables from
its surrounding scope with strong references to those values. You can
use a capture list to explicitly control how values are captured in a
closure.
...
Consider the following example, using a weak and a strong capture of two class type instances
class MyClass {
var myInt : Int = 0
func printMyInt() {
print(myInt)
}
}
func getClosure(a: MyClass, _ b: MyClass) -> (() -> ()) {
return { [weak aa = a, bb = b] in
aa?.printMyInt() ?? print("Lost reference")
bb.printMyInt()
}
}
func foo() -> (() -> ()) {
let a = MyClass()
let b = MyClass()
let closure = getClosure(a, b)
closure() // 0, 0
a.myInt = 1
b.myInt = 2
closure() // 1, 2
return closure
}
If foo() is called, then at the return of closure, MyClass instances a and b are out of scope. In the closure itself, aa keeps a weak reference to a, so a (memory) will be "destroyed" by ARC, and aa will become nil.
However, since the closure closes over b with a strong reference, ARC will retain the memory for b until the closure itself goes out of scope.
let closure = foo()
closure() // Lost reference, 2 <-- OK, expected
/* 'b' (not the reference, but the object in memory) still lives, but
cant be explicitly accessed? */
Hence my question: how to access, in this case, bb within the closure.
What I've tried
I've tried without success using Mirror:
var bar = Mirror(reflecting: closure)
print(bar.children.count) // 0
Also, I know we can "access" bb in the example above by adding a return type (MyClass) to the closure, but I'm wondering if we can actually access it without such a workaround (hence the ()-return specific closure in the question above).
Where I've looked prior to posting this question
I've been searching around SO for a possible existing threads that asks and answers this question, but the closest one I found,
Getting data out of a closure in swift,
don't really answer my question here. (Or perhaps the workarounds in the answers to that question does, and the answer to my question above is "you can't"?)