one interesting phenomenon about swift Closure - swift

I have a class object in the controller, and then I have a closure in this object.
I assign a function of the controller to the object's closure, and then the page does not deinit.
How can I solve this problem?
import UIKit
class SecondViewController: UIViewController {
let test = TestObject()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.test.select = self.selectButton(index:)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.test.doSomethine()
}
func selectButton(index:Int){
print(index)
}
deinit {
print("deinit")
}
}
import UIKit
typealias selectBtnBlock = (_ index:Int)->()
class TestObject: NSObject {
var select:selectBtnBlock?
func doSomethine(){
self.select!(1)
}
}

This is because your test object's select closure strongly captures your SecondViewController when you do the following:
self.test.select = self.selectButton(index:)
I recommend you do some reading about weak and strong types via Apple's Swift language reference. The "interesting phenomenon" you encountered is called a strong reference cycle.
Essentially, since Swift uses ARC as its memory management model, any object that is referenced by at least one other object else will be kept alive, and its memory not deallocated.
In your case, test has captured its parent SecondViewContoller via the line I mentioned. What that means is you have a situation like the following:
SecondViewController -> (owns) test // since its a member of the class
test -> (strongly captures) SecondViewController // via the assignment
This causes a strong reference cycle between the two, and does not allow ARC to deallocate either.
When it (ARC) tries to free up test, is knows that SecondViewController references it, so it can be freed only if the parent is also freed. When it tries to deallocate SecondViewController, ARC knows that this object is referenced by test.select closure.
Since both have a reference count greater than one, neither will get deallocated.
One way to solve your issue is to write:
self.test.select = {
[weak self] // weakly capture self, this prevents a ref cycle
(i:Int)->() in // a closure that accepts an Int
guard let s = self else { return } // check if self is not nil
s.selectButton(index: i) // finally invoke the required method
}
Another way, similar intent:
self.test.select = { [weak self] i in
self?.selectButton(index: i)
}
The weak keyword in this context is used to tell the Swift compiler that I do not want to keep a strong reference to what I am capturing (self in this case).

Related

Why Swift don't automatically handle memory leaks?

When I started developing Swift code I wasn't that experienced handling memory leaks, so it take some time to me to figure out that what is a retain cycle, what is ARC, and why I should use weak or unowned inside my closures that was creating those retain cycles.
By default I always add this piece of code in closures that is referencing self:
class MyController: UIViewController {
var myClosure: (Data?, Error?)?
override viewDidLoad() {
self.myClosure = { [weak self] (data, err) in
guard let self = self else { return }
self.present(someVC, animated: true)
}
}
}
That code is something very common and using this weak modifier and also unwrapping self is something that is almost a default code.
That makes me question. If I have to always add a weak self in code that is referencing self and not allowing the class to be deinit WHY Apple don't make it a default behaviour on the language so we don't need to have this code repeating everywhere on our code base?
You don't have to always use weak self when referencing self in a closure.
But in this particular case you do have to and it was explained here

How to use a class method as a closure without holding a strong reference to self in that method/closure?

I have a class :
class myVC:UIViewController {
let myButton = MyButton()
func viewDidLoad()
{
view.addSubview(myButton)
myButton.addTarget(myMethodClosure) // ***
}
func myMethodClosure() // I guess this method captures self strongly when im using it as a closure, tell me if im wrong
{
self.doingStuffs()
}
}
My Button Class :
class MyButton:UIView {
var cbk:(()->Void)?
init()
{
// ...
addTapGestureRecognizer(#selector(onPress))
}
func addTarget(_ cbk:()->Void))
{
self.cbk = cbk
}
func onPress() {
// execute animation of the button
// ... and after it :
self.cbk?()
}
}
When I present myVC and then dismiss it after, the VC is not deallocated. I guess this is because myMethodClosure, taken like a closure (not a method) on line *** holds a strong reference on self (that is : myVC).
So it gets like :
rootview -> myVC -> myButton -> myMethodClosure(held in variable cbk)
^ |
| |
--------------------------
So as myVC has 2 references it does not get deallocated when rootview release its reference to it with rootview.dismiss()
My question is : when I pass the method as a closure line //***, how to say "I want to pass it like a closure, but holding a weak reference to self"
I tried this : myButton.addTarget([weak self] myMethodClosure) of course it didn't work...
As usual, it's simplest to see what's going on here by eliminating all the unnecessary dross. Here's a reduced version of what you're doing:
class MyVC:UIViewController {
let myButton = MyButton()
override func viewDidLoad() {
view.addSubview(myButton)
myButton.cbk = myMethodClosure
}
func myMethodClosure() {
print(self)
}
deinit {
print("deinit")
}
}
class MyButton:UIView {
var cbk:(()->Void)?
}
We can test that by saying
let what = MyVC()
what.loadViewIfNeeded()
At that point, what goes out of existence, but "deinit" is not printed. We've got a retain cycle. The MyVC instance has a strong reference to the MyButton instance thru myButton, but the MyButton instance has a strong reference to its cbk function which has a strong reference to the MyVC instance (because the function refers to self).
There are various ways to break the cycle, but if you want to do it at the level of the function, you need to express the function as an anonymous function so that you can take advantage of [weak self]:
myButton.cbk = { [weak self] in self?.myMethodClosure() }
There probably really isn't any really good reason why a function with a name (i.e. defined by func) can't have a capture list, but currently Swift has no provision for doing that.

Is method closure retaining the instance in swift?

In swift, I can use instance methods as closures, for example, assigning the method to a callback
self.someView.someCallback = self.doSomething
So, is self strongly referenced here in self.doSomething? Does the line above create a reference loop?
There are two possible scenarios based upon your code snippet:
If doSomething is a instance method of self, then, yes, that line establishes a strong reference. Remember that Closures are Reference Types. You can easily confirm this and is easily confirmed empirically. Consider:
class ViewController: UIViewController {
var foo: (() -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
foo = bar
foo?()
}
func bar() { ... }
}
If I present and dismiss this view controller three times, and then use Xcode’s “Debug Memory Graph”, , I will see those three instances still lingering in memory (on the left) and if I select one, it will show me the strong reference cycle visually in the center panel:
And because I used the “Malloc stack” feature, on the right panel I can see precisely where the lingering strong reference is, namely in viewDidLoad where I set that closure.
However, if doSomething is not a function, but rather is a closure, then that line, itself, does not establish a strong reference, but rather it becomes a question of whether the closure, itself, refers to self and, if it does, whether there is a [weak self] or [unowned self] capture list or not. For more information, see Strong Reference Cycles for Closures.
In order to have a retain cycle, you need to have a strong reference on each direction, i.e.:
Object A strongly references Object B
Object B strongly references Object A
Assuming self in the code you shared is a View Controller, and assuming someView is a strong reference to a view, we could say that:
Object A (View Controller) strongly references Object B (Some View)
Now if Object B (Some View) has a strong reference back to the View Controller, you will have a retain cycle.
Assuming doSomething is a method in your ViewController, and not a closure, you will have a retain cycle
An easy way to check this, is by implementing deinit in both your Some View and your View Controller, like so:
class SecondViewController: UIViewController {
var someView: CustomView?
override func viewDidLoad() {
super.viewDidLoad()
someView = CustomView(frame: view.frame)
someView?.someCallback = doSomething
}
func doSomething() {
}
deinit {
print(#function)
}
}
final class CustomView: UIView {
var someCallback: (() -> Void)?
deinit {
print(#function)
}
}
You will see that the prints on deinit are never printed out in the console. However changing the way you assign someCallback to:
someView?.someCallback = { [weak self] in
self?.doSomething()
}
will cause deinit to run, thus breaking the retain cycle
Edit:
Or even, as an alternative:
weak var weakSelf = self
someView?.someCallback = weakSelf?.doSomething
(Even though this is using a weak reference, because this expression is evaluated at the time the assignment of someCallback is performed, not at the time it is executed, this will still become a strong reference) - Thanks #Rob
In Swift, declare a closure type variable, and would like to assign a func to it, prevent from the retain issue,
just do as follow, search the answer for all day long, eager to share:
self.someView.someCallback = {
[unowned self] in self.doSomething()
}

Swift - Expecting a leak after strongly capturing self in closure

Can anyone please explain why this doesn't leak?
I'm capturing self within a closure so I would have two strong pointers pointing at each other, therefore, the deinit message shouldn't ever be called for the Person object.
First, this is my class Person:
class Person {
var name: String
init(name: String) { self.name = name }
deinit { print("\(name) is being deinitialized") }
}
And this is my ViewController's implementation:
class ViewController: UIViewController {
var john:Person?
func callClosureFunction( closure:(name:Bool) -> () ) {
closure(name: true)
}
override func viewDidLoad() {
super.viewDidLoad()
john = Person(name:"John")
self.callClosureFunction { (name) in
self.john?.name = "John Appleseed"
self.john = nil
// xcode prints - John Appleseed is being deinitialized
}
}
}
I was expecting to be able to fix the issue by doing:
self.callClosureFunction { [weak self] (name) in ...
But that wasn't even necessary. Why?
Since your view controller is not retaining the closure, there is no circular reference. If you wrote this:
class ViewController: UIViewController {
var john:Person?
var closure:(Bool)->()?
func callClosureFunction( closure:((name:Bool) -> ())? ) {
closure?(name: true)
}
override func viewDidLoad() {
super.viewDidLoad()
john = Person(name:"John")
closure = { (name) in
self.john?.name = "John Appleseed"
// Because this closure will never be released, the instance of Person will never deinit either
}
self.callClosureFunction(closure)
}
}
then the view controller would retain the closure and the closure would retain the view controller via its reference to self. Therefore, neither would be released, and if you don't explicitly set self.john = nil (which you did in your original example), then the Person instance would never get deninit called.
It's quite common to inappropriately use weak self in closures when not necessary (and this can actually lead to some obscure bugs). The key rule to remember is that weak references are not the default in general under ARC. Strong should be the default unless it would lead to a retain cycle, in which case weak should be used only to break that circular reference. Same for closures: strong self should be the default, unless the self in this case also has a strong reference to the closure itself.
You're capturing self which points to ViewController, but you're wondering about the Person instance.
Person is actually not circular referenced and therefore gets de-initalized and released just fine when you set it to nil at the end of your closure.
Implement deinit for ViewController and see how that works.
I'm capturing self within a closure so I would have two strong pointers pointing at each other, therefore, the deinit message shouldn't ever be called for the Person object.
No, you have one strong pointer, from the closure to self. There's no cyclic reference back from the closure to self. Thus, you have a directed acylic graph, which is no problem for ARC.
However, your experiment is flawed, from the get-go. Even if the closure was captured, the John Appleseed Person object would still deinit. This object's lifecycle is exclusively dependent on on the john reference from your ViewController. When you set that reference to nil, you're removing the last reference to the John Appleseed object, thus it's deinitialized.

Strong Reference Cycles for Closures?

Swift Closure will have a strong reference cycle when it refers to self like this example:
class Test {
var name = "Hello"
func doSomething() {
{() -> Void in
self.name = "otherName"
}()
}
}
In the previous example, I created a strong reference cycle so I have to fix it with:
class Test {
var name = "Hello"
func doSomething() {
{[unowned self] () -> Void in
self.name = "otherName"
}()
}
}
Question: If I refer self in a closure do I have to use alway unowned self or are there cases where I have to use weak self?
If I refer self in a closure do I have to use alway unowned self or are there cases where I have to use weak self?
Neither. In most cases, just refer to self normally and do nothing with its memory management. You only have to worry about memory management if there is a danger of a retain cycle, and unless you store the closure somewhere, such as a property of self, there is no such danger.
You can easily prove this by adding a deinit implementation:
class Test {
var name = "Hello"
func doSomething() {
{() -> Void in
self.name = "otherName"
}()
}
deinit {
println("bye")
}
}
Now make a Test instance and immediately release it:
func testTest () {
let t = Test()
}
You see "bye" in the console, proving that the instance was released in good order. There was never any kind of "strong reference cycle" in this code. Your concerns are groundless.
[By the way, you are using the word "closure" wrong. Every Swift function is a closure. If there were a retain cycle issue merely because of using the word self in a closure, every Swift function would be subject to this issue - and clearly that is not the case. The place where weak and unowned self comes into play is in an anonymous function - and only, as I said before, if that anonymous function is itself also retained by self.]