Swift closures causing strong retain cycle with self
Yes, that can still cause a retain cycle.
The simplest retain cycle is 2 objects that each have strong references to each other, but 3-way and larger retain cycles are also possible.
In your case, you have view controller who's view contains a button (a strong reference.) The button has a strong reference to a closure. The closure strongly references the view controller using self. So the view owns the button. The button owns the closure. The closure owns the view controller. If you dismiss the view controller (say it was a modal) then it SHOULD be deallocated. However, since you have this 3-way retain cycle, it won't be deallocated. You should add a deinit method to your view controller with a print statement and try it.
The solution is to add a capture list (The [weak self]
bit) just like you did in your first example.
Note that a common pattern is to add a capture list, and then map the weak variable to a strong variable inside the closure:
let myClosure = { [weak self] in
guard let strongSelf = self else { return }
//...
strongSelf.doSomething()
}
That way, if the closure is still active but the object that owns it was released, the guard statement at the beginning detects that self is nil and exits at the beginning of the closure. Otherwise you have to unwrap the optional every time you refer to it.
In certain cases it's also possible that the object in the capture list (self in these examples) could be deallocated in the middle of the closure being executed, which can cause unpredictable behavior. (Gory details: This is only when the closure is run on a different thread than the owner of the object in the capture list, but completion handlers are pretty commonly run on a background thread, so it does happen)
Imagine this:
let myClosure = { [weak self] in
self?.step1() //1
//time-consuming code
self?.property = newValue //2
//more time-consuming code
self?.doSomething() //3
//even more time-consuming code
self?.doSomethingElse() //4
}
With the code above, if the closure is run on a background thread, its possible that self would still be valid at step 1, but by the time you execute step 2, self has been deallocated. The same goes for steps 3 and 4. By adding the guard strongSelf = self else { return }
at the beginning of the closure you test at the entry of the closure to make sure self is still valid, and if it is, you make the closure create a strong reference that only lives as long as the closure takes to run, and that prevents self
from being deallocated while the closure code is executing.)
Weird weak self and retain cycle behaviour
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.
Why am I getting a retain cycle from passing weak self in to a static function's closure?
try comment this line
self?.configureCollectionView()
mb it's the problem, because [weak self]
is enough for fix retain in this closure
closure and retain cycle
You're correct. B is retaining A, but A is retaining as its b_code
property a function that refers to (captures) B. That's a retain cycle.
I am trying to pass a closure from one class to another
No you're not. In this line
a.b_code = someCode
the term someCode
is not a closure. It is a method reference. That's the trouble.
Change
a.b_code = someCode
To
a.b_code = { [weak self] in print(self?.b_value) }
Or possibly
a.b_code = { [weak self] in self?.someCode() }
The point is that you must assign to b_code
an anonymous function (what you call a closure), not a method reference, because that's the only way you can have a capture list that lets you manage the memory of the captured reference to self
to break the retain cycle.
Passing self and returning it in a closure without retain cycle
No, from what I can see, this will not result in a retain cycle.
For example, this will result in a retain cycle. When myFunction
executes, the Foo
instance will hold a strong reference to the completion
block. In turn, the completion
block holds a strong reference to the Foo
instance. This will a retain cycle, and in this case you need to add a capture list.
class Foo<T> {
var completion: (T) -> ()
func myFunction(_ vc: T, completion: @escaping (T) -> ()) {
self.completion = completion // This will create a retain cycle
completion(vc)
}
func doSomething() {
myFunction(self) { vc in
print(vc)
}
}
}
Will this cause a retain cycle Swift?
This wouldn’t cause a retain cycle because it looks like a static method on a different type. So the getTextFileData
method will temporarily retain the closure you are passing in until any asynchronous work is complete, and the closure will temporarily retain self. But when the work is complete and the closure is done executing, those temporary retains will expire and automatic memory management can clean up as appropriate.
The danger of a retain cycle is when you have a closure the that references / captures self, and self also retains the closure, for example. Like this:
class GameController {
var games: [Game]?
// self retains the closure as a property
let updateClosure:([Game], Bool)->() = {
games, success in
if success {
self.games = games // and the closure captures self. Each retains each other indefinitely, this is a retain cycle and neither this closure nor self will ever be deallocated
}
}
func load() {
NPWebService.getTextFileData(updateClosure)
}
}
So normally weak self or unowned self in closure capture lists are only needed:
- if self or something retained by self will be retaining that closure (this is rarely the case when a closure is created locally and transiently at the call site to pass into a method as an argument)
- if for non-retain cycle reasons, the desired behavior is to allow self to be deallocated before some asynchronous work is completed, and before the closure is invoked. In this case the closure shouldn’t strongly capture self and instead should weakly capture it and check if it still exists before making use of self when the closure is invoked
swift - retain cycle when calling inner function?
Do I have a retain cycle
Yes, in the sense that you probably mean it. self
(the SomeClass) is retaining the Manager, and the Manager, once doSomething
has been called, is retaining the callback method progressComplete
which refers to self
.
So, it's not a permanent situation (a true leak), but SomeClass cannot go out of existence until such time as the Manager's requestData
has performed its callback. It is easy to confirm this experimentally, and you can see it in the memory graph:
That's not necessarily a bad thing! But if it's going to be a problem, then you need to fix it.
However, note that weak self
won't help you here, as there is no place to say it. You can only say weak self
in the body of an anonymous function — and you don't have any anonymous functions!
So the correct way to write this, if you are concerned about it, would be to abandon your separate progressComplete
function and write the callback as an anonymous function, and do the weak–strong dance, like this:
self.manager.requestData { [weak self] success in
if let self = self {
self.someVar = nil
completion?(success)
}
}
Related Topics
Argument Labels Do Not Match Any Available Overloads
Swift Error: Missing Return in a Function Expected to Return 'String'
Autolayout Contraints for a View from Xib
Skphysicsbody Avoid Collision Swift/Spritekit
Does a Mutating Struct Function in Swift Create a New Copy of Self
Firestore Order by Time But Sort by Id
Initialize Lazy Instance Variable with Value That Depends on Other Instance Variables
Why am I Allowed Method Access Less Restrictive Than Class Access
How to Integrate Uiactivityviewcontroller with Swiftui's Scrollview
I Won't Be Able to Return a Value with Alamofire in Swift
Localizewithformat and Variadic Arguments in Swift
How to Create Several Cached Uicolor
What's the Equivalent to String.Localizedstringwithformat(_:_:) for Swiftui's Localizedstringkey
How Does Dictionary Use the Equatable Protocol in Swift
Getting Optional("") When Trying to Get Value from Keychain
Uicollectionview Compositionallayout Not Calling Uiscrolldelegate