Weird weak self and retain cycle behaviour - swift

Let's consider following code:
// Just for easier testing
protocol Printer {
var delayer: Delayer { get }
}
// Retain cycle
class Printer1: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
return Delayer(action)
}()
deinit {
print("deinit")
}
}
// Works fine, but weak mess
class Printer2: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
return Delayer { [weak self] in self?.action() }
}()
deinit {
print("deinit")
}
}
// Questionable hack, but works fine
class Printer3: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
return Delayer(weakAction)
}()
// computed property or function is also fine here
private lazy var weakAction: () -> Void = {
return { [weak self] in
self?.action()
}
}()
deinit {
print("deinit")
}
}
// Retain cycle
class Printer4: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
weak var welf: Printer4? = self
return Delayer(welf?.action ?? {})
}()
deinit {
print("deinit")
}
}
// Works fine
class Printer5: Printer {
private func action() {
print("action")
}
private(set) lazy var delayer: Delayer = {
weak var welf: Printer5? = self
return Delayer { welf?.action() }
}()
deinit {
print("deinit")
}
}
class Delayer {
private var action: () -> Void
init(_ action: #escaping () -> Void) {
self.action = action
}
func run() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
self?.action()
}
}
}
So we have a Printer class which contains a Delayer class that takes the action on Printer and performs it delayed.
We call it something like this:
var printer: Printer? = PrinterX()
printer?.delayer.run()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
printer = nil
}
It is clear why Printer1 creates retain cycle. Action is passed into delayer with implicit strong self which cannot be released because Delayer is owned by Printer.
Printer2 is the intended way in my opinion. Obviously doesn't create retain cycle, but it is kind of mess to write all the time. Thats why I started experimenting with other solution.
I don't understand why Printer3 doesn't create retain cycle. Because weakAction is property on self, so passing it like that into Delayer should create strong reference like in Printer1.
I also don't understand why Priner4 does create retain cycle. welf is local weak reference to self, so it should not increase the reference count when passing it into the Delayer.
Strangely enough using the welf inside closure in Printer5 doesn't create retain cycle.
Questions
Can anyone please explain to me this weird behavior on Printer3, Printer4 and Printer5
I am tempted to use the Printer3 solution. Is it safe to use? As it seems almost like a bug, can I use it without worrying about it being fixed in future versions and therefore creating retain cycle in my app?

First of all, all printers are creating and retaining their own Delayer. The delayer takes a closure and, in turn, retains that closure.
Let's try to walk through them one by one.
Printer1
As you stated yourself, it's pretty clear why it's creating a retain cycle. You are passing the self.action instance method as the closure to the Delayer, and since all closures are reference types, passing self.action will retain its surrounding scope (which is Printer1).
Printer2
Again, pretty obvious here. You're explicitly capturing a weak reference to self inside the closure you're passing to Delayer, hence not creating a retain cycle.
Printer3
Here, a retain cycle is not created, because the self.weakAction property is called immediately, and its result (a closure which holds a weak reference to self) is passed on to Delayer. This, in effect, is the exact same thing as what's happening in Printer2.
Printer4
First, you're capturing a weak reference to self, and then fetching welf?.action and passing the result into Delayer. Again, welf?.action is called immediately, and the result (a pointer to an instance method) is passed on to Delayer. The weak reference to self is only kept for the duration of the surrounding scope (the lazy var creation scope), and passing the action instance method will retain self. This is identical to Printer1.
Printer5
Here, you're first creating a weak reference to self, and then you're capturing that weak reference inside a new closure that is passed to Delayer. Since self is never directly referenced in the passed closure, it will not capture self in that scope, only the welf weak reference. This is pretty much identical to Printer2, but with a slightly different syntax.
Personally, I would opt for the Printer2 way (creating a new closure, retaining a weak reference to self and using that to call self?.action). It makes for the easiest code to follow (as opposed to retaining a variable with a closure that weakly captures self). But, depending on what you're actual use case is, it might of course make sense.

Related

Swift - Holding closure reference leaks

Edit (after discussion):
It's a common practice in SwiftUI to have a View (struct) hold an ObservableObject. Since a struct is not a reference type, it's easy to forget that is holds one.
The questions should be really:
Can an indirect references cause a retain cycle?
Can a retain cycle involve a single pointer
The answer to both is yes.
struct -> class -> closure{struct.class}
class ViewModel: ObservableObject
...
struct MyView: View {
#ObservedObject var viewModel: ViewModel
var body: some View {
Button("Action") {
viewModel?.handler {
// ViewModel is retained here through self:
// (MyView.viewModel)
self.someAction()
}
}
}
}
In SwiftUI, it's not a good practice to 'pass yourself' to your ViewModel
Class holding an instance var pointing to itself
(discussed here How does ARC deal with circular linked lists when the external head reference is removed?)
class A {
var next: A
}
let a = A()
a.next = a // Retain cycle of an object to itself.
(Original question)
Why is holding a reference to a closure cause a memory leak even if the closure does not retain anything?
(This started with a SwiftUI issue, but I'm not sure it's related really)
So here's a simple Manager (ViewModel) that holds a closure.
class Manager: ObservableObject {
private var handler: (() -> Void)?
deinit {
print("Manager deallocated")
}
func shouldDismiss(completion: #escaping () -> Void) {
handler = completion
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.handler?()
}
}
}
Here's a view, using it:
struct LeakingView: View {
#ObservedObject var manager: Manager
let shouldDismiss: () -> Void
var body: some View {
Button("Dismiss") { [weak manager] in
manager?.shouldDismiss {
self.shouldDismiss()
}
}
}
}
Here's a ViewController activating the flow:
class ViewController: UIViewController {
var popup: UIViewController?
#IBAction func openViewAction(_ sender: UIButton) {
let manager = Manager()
let suiView = LeakingView(manager: manager) { [weak self] in
self?.popup?.view.removeFromSuperview()
self?.popup?.dismiss(animated: true)
self?.popup = nil
}
popup = LKHostingView(rootView: suiView)
popup?.view.frame.size = CGSize(width: 300, height: 300)
view.addSubview(popup!.view)
}
}
When running this, as long as the manager is holding the closure, it will leak and the Manager will not get released.
Where is the retain cycle?
Why is holding a reference to a closure cause a memory leak even if the closure does not retain anything?
Because the closure does retain something. It retains self.
For example, when you say
manager?.shouldDismiss {
shouldDismiss()
}
The second shouldDismiss means self.shouldDismiss and retains self. Thus the LeakingView retains the Manager but the Manager, by way of the closure, is now retaining the LeakingView. Retain cycle!
This is why people say [weak self] in at the start of such closures.
I would suggest that you might not want to use [weak self] pattern here. You are defining something that should happen ½ second after the view is dismissed. So you probably want to keep that strong reference until the desired action is complete.
One approach is to make sure that Manager simply removes its strong reference after the handler is called:
class Manager: ObservableObject {
private var handler: (() -> Void)?
func shouldDismiss(completion: #escaping () -> Void) {
handler = completion
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.handler?()
self.handler = nil
}
}
}
Alternatively, if the use of this completion handler is really limited to this method, you can simplify this to:
class Manager: ObservableObject {
func shouldDismiss(completion: #escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
completion()
}
}
}
Or
func shouldDismiss(completion: #escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: completion)
}
All of these approaches will make sure that Manager does not keep any strong references beyond the necessary time.
Now, the caller can choose to use [weak self] if it wants (i.e., if it doesn’t want the captured references to even survive ½ second), but if you really want the “do such-and-such after it is dismissed”, it probably wouldn’t use weak references there either. But either way, it should be up to the caller, not the manager. The manager should just ensure that it doesn’t hang on to the closure beyond the point that it is no longer needed.

Swift: Assign a class method to a handler and handling weak self

I just went memory-leak hunting in the app I am working on, and noticed that the following produces a memory leak:
class SubClass {
var didCloseHandler: (() -> Void)?
}
class MainClass {
var subClass = SubClass()
func setup {
subClass.didCloseHandler = self.didCloseSubClass
}
func didCloseSubClass() {
//
}
}
This produces a retain cycle, and for good reason - didCloseHandler captures MainClass strongly, and MainClass captures SubClass strongly.
My Question: Is there a way in Swift that allows me to assign a class method to a handler without a retain cycle?
And yes, I am aware that I can do this using subClass.didCloseHandler = { [weak self] self?.didCloseSubClass() }. I'm wondering, though, if it can be done without introducing a new closure.
make a weak reference of subClass in MainClass
If you don't have strong reference to SubClass instance somewhere else - you may try wrapper like this:
func WeakPointer<T: AnyObject>(_ object: T, _ method: #escaping (T) -> () -> Void) -> (() -> Void) {
return { [weak object] in
method(object!)()
}
}
Then use it like this:
func setup() {
subClass.didCloseHandler = WeakPointer(self, MainClass.didCloseSubClass)
}
If you don't need properties from MainClass instance in didCloseSubClass implementation - you can make this method static, which will also solve your problem.
If you have strong reference to SubClass instance somewhere else and it won't be deallocated immediately - weak var subClass will do, as was already mentioned.
EDIT:
I've come up with another idea. It may look a bit more complicated, but maybe it would help.
import Foundation
class SubClass {
#objc dynamic func didCloseHandler() {
print(#function)
}
deinit {
print(" \(self) deinit")
}
}
class MainClass {
var subClass = SubClass()
func setup() {
if let implementation = class_getMethodImplementation(MainClass.self, #selector(didCloseSubClass)),
let method = class_getInstanceMethod(SubClass.self, #selector(SubClass.didCloseHandler)) {
method_setImplementation(method, implementation)
}
}
#objc func didCloseSubClass() {
print(#function)
}
deinit {
print(" \(self) deinit")
}
}
You change closure for #objc dynamic method and set it's implementation to the one from MainClass in setup().

EXC_BAD_ACCESS on waitForExpectations while unit testing

Consider the following, simplified structure:
class MyClass {
weak var delegate: MyClassDelegate?
}
protocol MyClassDelegate: class {
func goneWrong()
}
And the test case that throws the EXC_BAD_ACCESS (0x40dedeadbec0) error:
class MyTest: XCTestCase {
func test() {
let exp = expectation(description: "Expecting a call")
let a = MyClass()
a.delegate = MyMockDelegate(exp: exp)
// Thread 1: EXC_BAD_ACCESS(code=1, address=0x40dedeadbec0)
waitForExpectations(timeout: 10)
}
class MyMockDelegate: MyClassDelegate {
let exp: XCTestExpectation
init(exp: XCTestExpectation) {
self.exp = exp
}
func goneWrong() {
self.exp.fulfill()
}
}
}
The error is not thrown if the delegate var is declared normally, without the weak keyword. The problem is that it needs to be weak to avoid the strong reference cycle in the real code (not the test case). How do I go around this?
This is an old question, so I'm sure you solved it by now, but for the sake of future readers, the problem is that you've defined your delegate to be weak (i.e. allow it to be set to nil and the object deallocated as soon as their are no strong references). But you have no strong references to your MyMockDelegate instance:
let a = MyClass()
a.delegate = MyMockDelegate(exp: exp)
Now, I'd expect it to gracefully nil the delegate reference in this scenario, and I don't know why you got error at 0x40dedeadbec0 address, because I don't. (BTW, I don't think 0x40dedeadbec0 is a real memory address, but rather is one of those bad puns by some kernel developer.)
Anyway, if you expect your mock delegate to work, you need to keep a reference to it until the end of your test, e.g.:
class MyAppTests: XCTestCase {
func test() {
let exp = expectation(description: "Expecting a call")
var delegate: MyMockDelegate? = MyMockDelegate(exp: exp)
let a = MyClass()
a.delegate = delegate
// do something
waitForExpectations(timeout: 10)
delegate = nil
}
}
Alternatively, you can create a property in your test class to keep a strong reference to the delegate for the life of the test. Either way, you need more than a single weak reference if you don't want that disappearing on you.

Swift weak self function retention

If I have a closure that references a weak var weakSelf = self, can I change that closure to a direct function reference, through weakSelf?
struct ClosureHolder {
let closure: () -> Void
}
class ClosureSource {
func hello() {
NSLog("hello")
}
func createWeakSelfWithinInnerClosureClosureHolder() -> ClosureHolder {
weak var weakSelf = self
return ClosureHolder(closure: {
weakSelf?.hello()
})
}
func createWeakSelfDotHelloClosureHolder() -> ClosureHolder {
weak var weakSelf = self
// The code below won't compile because weakSelf is an Optional.
// Once I unwrap the optional, I no longer have a weak reference.
// return ClosureHolder(closure: weakSelf.hello)
// this strongifies the weak reference. :(
return ClosureHolder(closure: weakSelf!.hello)
}
}
Instead of createWeakSelfWithinInnerClosureClosureHolder, I'd prefer something like createWeakSelfDotHelloClosureHolder.
No you can't. Saying self.foo (if foo is a method) is exactly the same thing as to saying MyClass.foo(self). And methods curried in this fashion always keep a strong reference to the receiver object. If you want to maintain a weak reference, then you need to stick with the { weakSelf?.hello() } approach.

weak to non class types to avoid memory leak

I've got a memory leak in this case, if I pass a reference to any method, the self comes with it which increases it's reference count I guess, how can I make non class types to be weak
public class Observer {
weak private var method: ((message: AnyObject) -> ())! //error here
weak private var context: AnyObject!
}
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
in another class I guess self.callback creates a strong reference to the caller object and passes on.
var observer = Observer(method: self.callback, context: self) //pass of self.callback is a strong reference
Edit:
Working on the above, my attempt using an example that further clarifies the situation using two classes. deinit never gets called.
class Test {
private var ref: Observer?
init() {
ref = Observer(method: self.callback, context: self)
}
func callback(message: AnyObject) {
}
deinit {
println("deinit test")
}
}
public class Observer {
private var method: ((message: AnyObject) -> ())?
weak private var context: AnyObject!
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
deinit {
println("deinit observer")
}
}
From looking at your code, it seems like you are talking about a retain cycle where the Test object holds onto the Observer object through the variable ref, the Observer object holds onto the closure formed by doing self.callback, which holds onto self.
Generally in such cases, you don't want the closure property itself to be weak. Rather, you want the closure to capture a weak reference to self (the Test object is passing a "callback" to itself to another object). However, that is somewhat confusing here as we are not explicitly using closure syntax (rather, you are getting a closure by accessing a method on an instance and not calling it). The problem of capturing a weak reference to self in this case was covered in this question.
The best solution is:
ref = Observer(method: {[unowned self] in self.callback($0)}, context: self)
Try this:
public class Observer {
private var method: ((message: AnyObject) -> ())?
weak private var context: AnyObject!
public init(method: (AnyObject -> ())?, context: AnyObject?) {
self.method = method
self.context = context
}
}
I tried it and it doesn't create a strong reference cycle. But I also tried with ! instead of ?, and that didn't caused as well, and I hope somebody is out there to explain that.