Reputation: 936
so I have written some codes to better under retain cycle.
class AnotherViewController : UIViewController{
var closure : ((() -> Int) -> ())!
override func viewDidLoad() {
super.viewDidLoad()
closure = self.someFunctionWithNonescapingClosure
}
func someFunctionWithNonescapingClosure(closure: () -> Int) {
// closure()
}
}
So apparently, assigning one of the function in viewController to the property closure, it causes the retain cycle problem.
But I don know how?
Self has a strong reference to Closure But, does assigning a function in the viewController tp Closure, make a strong reference of self to closure?
Thanks
EDIT ------
Apparently, if you try this in a playground by creating the AnotherViewController, initialize it and assign it to a variable, then set the variable to nil, it will successfully deinit the AnotherViewController, but if you try it in an app, the AnotherViewController will not get deinited.
You can try to add a button to AnotherViewController and dismiss it, for convenience, the button code is this
private func addAButton(){
let button = UIButton()
let buttonBounds = CGRect(x: 0, y: 0, width: 200, height: 200)
let buttonCenter = view.center
button.bounds = buttonBounds
button.center = buttonCenter
view.addSubview(button)
button.backgroundColor = .red
button.addTarget(self, action: #selector(goBack), for: .touchUpInside)
}
@objc func goBack(){
dismiss(animated: true, completion: nil)
}
Upvotes: 0
Views: 923
Reputation: 53000
Your closure
is being assigned an instance method, which implicitly captures self
, hence the cycle.
Try the following app:
import Cocoa
class Cycle
{
var closure : ((() -> Int) -> ())!
init()
{
closure = self.someFunctionWithNonescapingClosure
}
func someFunctionWithNonescapingClosure(closure: () -> Int)
{
print("Hello") // breakpoint on this line
}
}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate
{
@IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification)
{
let aCycle = Cycle()
aCycle.closure({ 42 })
}
}
Add a breakpoint on the print
line and run the app.
The app will stop inside someFunctionWithNonescapingClosure()
which is called by aCycle.closure({ 42 })
.
Look at the variable display, there is a self
. This is because every instance method has an implicit self
argument.
In your code where does that self
come from?
When the line:
closure = self.someFunctionWithNonescapingClosure
is executed Swift captures the current value of self
to pass as the implicit argument to someFunctionWithNonescapingClosure()
, it must do this as you are creating a closure from an instance method.
And so you have your cycle, the closure assigned to closure
contains a reference to self
.
To see this note the value of self
when the debugger stops and then select the entry for applicationDidFinishLaunching
in the stack trace and look at its aCycle
variable - it has the same value as the self
of someFunctionWithNonescapingClosure
- there is your cycle.
In the stack trace you will also see entries like "partial apply" - this is where the supplied argument ({ 42 }
) and the implicitly captured self
are collected and passed to someFunctionWithNonescapingClosure()
.
If you change the code to:
init()
{
closure = Cycle.someFunctionWithNonescapingClosure
}
static func someFunctionWithNonescapingClosure(closure: () -> Int)
{
print("Hello") // breakpoint on this line
}
that is make someFunctionWithNonescapingClosure
a class (static
) method instead on an instance one then the current class instance is not captured in the closure and you will not get a cycle.
HTH
Upvotes: 1