We're trying to use Swift structs where we can. We are also using RxSwift which has methods which take closures. When we have a struct that creates a closure that refers to self, that creates a strong reference cycle.
import Foundation
import RxSwift
struct DoesItLeak {
var someState: String = "initial value"
var someVariable: Variable<String> = Variable("some stuff")
let bag = DisposeBag()
mutating func someFoo() {
someVariable.subscribeNext { person in
self.someState = "something"
}
.addDisposableTo(bag)
}
}
How do I know this? If I create 100,000 DoesItLeak objects and call someFoo() on each of them, I believe I have 100,000 objects with strong reference cycles. In other words, when I get rid of the DoesItLeak array containing those objects, the objects stay in memory. If I do not call someFoo(), there is no problem.
Variable is a class. So, I can see this memory issue by using xcode's Instruments' Allocations and filtering in Variable< String >
If I try to use [weak self] such as in the following, I get a compiler error:
someVariable.subscribeNext { [weak self] person in
The compiler error is "weak cannot be applied to non-class type"
In real/non-example code, we access methods and variables via self and it's a memory issue.
How can I resolve this memory issue while keeping the DoesItLeak a struct?
Thanks for your help.
Variable
type a class or struct? – Marc KhadpesomeVariable
property of the struct is captured weakly, but I'm not aware of a way to do that. – Marc KhadpeDoesItLeak
to be a value type which means it may exist only on a stack frame. So what do we pass asself
to the closure? A pointer to an object on the stack frame, and then we hope like hell that the stack frame is still around when the closure executes. You can actually do that in Swift, but it's completely unsafe. We'd likeDoesItLeak
to be a value type, but as soon as it's passed to a closure on a reference-counted object then it must also be reference-counted value type. Therefore,DoesItLeak
can't be a struct. – Darren