Swift Array containing closures - swift

I wrote a state machine class and added the possibility to add an onChange handler:
private var handlers = [((UserState) -> Void)]()
func onChange(handler: #escaping ((UserState) -> Void)) {
self.handlers.append(handler)
}
func changeState(to newState: UserState) {
print($0.self)
$0(newState)
}
And I am adding a change listener like this in a view controller:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UserStateMachine.shared.onChange { newState in
self.setIcon(newState.premium)
}
}
Is the closure now owned by the view controller which added it or by the state machine? Do I have to add weak references to the handlers elements?

To answer your first question very directly, the closure is owned by your state machine class. The state machine is what retains the closure and the closure captures a strong reference to your view controller.
Yes, you will need to add a weak reference here since your shared state machine appears to be a singleton that will live forever, unless you had some mechanism for deregistering and removing the closures before the view controller was dismissed.
It rarely makes sense for a singleton to be retaining references to view controllers, however, so I would recommend adding a [weak self].

You capturing the self in escaping closure, so yes, you should handle this situation with weak (or unowned).
Also, you can check if the controller deinits with no problem (i.e. controller calls the deinit) - all closures in controller properly handled. If deinit never called - you should think about missing weak/unowned in available closures.

Related

Swift closures causing strong retain cycle with self

I just want to know if I am understanding this correctly or not. So according to the apple docs when you create a closure as a property of a class instance and that closure references self(The class that created the closure property) this would cause a strong retain cycle an ultimately the class nor the closure will ever get released. So in laymen terms that means that if I have a class that has a property and that property is a closure, and once I assign the functionality of that closure within the class that declares the closure property that will cause a strong retain cycle. Heres a quick example of what I mean
class SomeViewController{
let myClosure:()->Void
public func someFunction(){
....bunch of code
myClosure = {
self.dismiss(blahBlahBlah)
}
}
}
This ultimately causes a retain cycle since the closure keeps a strong reference to self, which is the class that creates the closure property. Now to fix this according to apple I would define a capture list like so
class SomeViewController{
let myClosure:()->Void
public func someFunction(){
....bunch of code
myClosure = { [weak self] in
self?.dismiss(blahBlahBlah)
}
}
}
Notice how I put the [weak self] before the in statement. This lets the closure know to only hold a weak reference to self and not a strong reference. IM supposed to use weak when self can out live the closure or unowned when the closure and self live for the same duration.
I got this information from here Automatic Reference Counting and in the Strong Reference Cycles for Closures section of that link, theres this sentence "A strong reference cycle can also occur if you assign a closure to a property of a class instance, and the body of that closure captures the instance" Im about 90% sure Im understanding this correctly but theres just that 10% of doubt. So do I have this correct?
The reason why im asking this is because I use callbacks for some of my buttons in my views. And those callbacks call to self but the self in that scenario is the view controller that is responding to the callback not the actual view itself. This is where im doubting myself because I from that sentence I highlighted I don't think I need to put [weak self] on all those button callbacks, but im just making sure. Heres an example of this
class SomeViewController {
let someSubview:UIView
override viewDidLoad() {
//Some Subview has a button and in that view I just have some action that gets fired off calling the callback here in the view controller I don't need to use the [weak self] in this scenario because closure property is not in this class correct?
someSubview.someButtonsCallback = {
....run code then
self?.dismiss(blahBlahBlah)
}
}
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.)

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()
}

Confusion Regarding How to Use Capture Lists to Avoid a Reference Cycle

My custom UIViewController subclass has a stored closure property. The closure signature is defined as to take a single argument of the same type of the class:
class MyViewController {
var completionHandler : ((MyViewController)->(Void))?
// ...
}
...the idea being, the object passing itself back as an argument of the handler, a bit like the UIAlertAction initializer.
In addition, and for convenience, I have a factory(-ish) class method:
class func presentInstance(withCompletionHandler handler:((MyViewController)->(Void)))
{
// ...
}
...that performs the following actions:
Creates an instance of the view controller,
Assigns the completion handler to the property,
Presents it modally from whatever happens to be the top/root view controller at the time of the call.
My view controller is definitely leaking: I set up a breakpoint on deinit() but execution never hits it, even way after I'm done with my view controller and it is dismissed.
I am not sure of how or where I should specify a capture list in order to avoid the cycle. Every example I have come across seems to place it where the closure body is defined, but I can't get my code to compile.
Where I declare the closure property? (how?)
var completionHandler : ((MyViewController)->(Void))?
// If so, where does it go?
Where I declare the closure parameter?
class func presentInstance(withCompletionHandler handler:((MyViewController)->(Void)))
{
// Again, where could it go?
Where I call the above function and pass the closure body?
MyViewController.presentInstance(withCompletionHandler:{
[unowned viewController] viewController in
// ...
})
// ^ Use of unresolved identifier viewController
// ^ Definition conflicts with previous value
Where I actually call the closure, towards self?
None of these will compile:
self.completionHandler?(unowned self)
self.completionHandler?([unowned self] self)
self.completionHandler?([unowned self], self)
Well, it turns out my view controller was being retained by a block, but not the one I was thinking:
class MyViewController
{
deinit(){
print("Deinit...")
}
// ...
#IBAction func cancel(sender:UIButton)
{
completionHandler(self)
// View controller is dismissed, AND
// deinit() gets called.
}
#IBAction func punchIt(sender: UIButton)
{
MyWebService.sharedInstance.sendRequest(
completion:{ error:NSError? in
self.completionHandler(self)
// View controller is dismissed, BUT
// deinit() does NOT get called.
}
)
}
...so it is the closure passed to MyWebService.sharedInstance.sendRequest() that was keeoing my view controller alive. I fixed it by adding a capture list like this:
MyWebService.sharedInstance.sendRequest(
completion:{ [unowned self] error:NSError? in
However, I still don't quite understand why the short-lived completion handler, passed to the web service class, executed once, and disposed, was keeping my view controller alive. That closure, not stored anywhere as a property, should be deallocated as soon as it is exited, right?
I must be missing something. I guess I'm still not fully thinking in portals closures.

Change #IBOutlet from a subview

I'm trying to enable or disable an #IBOutlet UIButton Item of a toolbar from a UIView.
The button should get disabled when the array that I'm using in EraseView.Swift is empty.
I tried creating an instance of the view controller but it gives me the error (found nil while unwrapping):
in EraseView:
class EraseView: UIView {
...
let editViewController = EditImageViewController()
//array has item
editViewController.undoEraseButton.enabled = true //here I get the error
...
}
I tried to put a global Bool that changed the value using it in EditImageViewController but it doesn't work:
var enableUndoButton = false
class EditImageViewController: UIViewController {
#IBOutlet weak var undoEraseButton: UIBarButtonItem!
viewDidLoad() {
undoEraseButton.enabled = enableUndoButton
}
}
class EraseView: UIView {
...
//array has item
enableUndoButton = true //here I get the error
...
}
I know it's simple but I can't let it work. Here's the situation:
The root of the problem is the line that says:
let editViewController = EditImageViewController()
The EditImageViewController() says "ignore what the storyboard has already instantiated for me, but rather instantiate another view controller with no outlets hooked up and use that." Clearly, that's not what you want.
You need to provide some way for the EraseView to inform the existing view controller whether there was some change to its "is empty" state. And, ideally, you want to do this in a way that keeps these two classes loosely coupled. The EraseView should only be informing the view controller of the change of the "is empty" state, and the view controller should initiate the updating of the other subviews (i.e. the button). A view really shouldn't be updating another view's outlets.
There are two ways you might do that:
Closure:
You can give the EraseView a optional closure that it will call when it toggles from "empty" and "not empty":
var emptyStateChanged: ((Bool) -> ())?
Then it can call this when the state changes. E.g., when you delete the last item in the view, the EraseView can call that closure:
emptyStateChanged?(true)
Finally, for that to actually do anything, the view controller should supply the actual closure to enable and disable the button upon the state change:
override func viewDidLoad() {
super.viewDidLoad()
eraseView.emptyStateChanged = { [unowned self] isEmpty in
self.undoEraseButton.enabled = !isEmpty
}
}
Note, I used unowned to avoid strong reference cycle.
Delegate-protocol pattern:
So you might define a protocol to do that:
protocol EraseViewDelegate : class {
func eraseViewIsEmpty(empty: Bool)
}
Then give the EraseView a delegate property:
weak var delegate: EraseViewDelegate?
Note, that's weak to avoid strong reference cycles. (And that's also why I defined the protocol to be a class protocol, so that I could make it weak here.)
The EraseView would then call this delegate when the the view's "is empty" status changes. For example, when it becomes empty, it would inform its delegate accordingly:
delegate?.eraseViewIsEmpty(true)
Then, again, for this all to work, the view controller should (a) declare that is conforms to the protocol; (b) specify itself as the delegate of the EraseView; and (c) implement the eraseViewIsEmpty method, e.g.:
class EditImageViewController: UIViewController, EraseViewDelegate {
#IBOutlet weak var undoEraseButton: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
eraseView.delegate = self
}
func eraseViewIsEmpty(empty: Bool) {
undoEraseButton.enabled = !empty
}
}
Both of these patterns keep the two classes loosely coupled, but allow the EraseView to inform its view controller of some event. It also eliminates the need for any global.
There are other approaches that could solve this problem, too, (e.g. notifications, KVN, etc.) but hopefully this illustrates the basic idea. Views should inform their view controller of any key events, and the view controller should take care of the updating of the other views.

Swift Delegation

I'm having trouble wrapping my head around delegation in Swift. After reading some guides, I was able to set it up delegation between two ViewControllers, but I'm not understanding how it works. In my first view controller, I have a a label that displays what has been entered in the second view controller which contains a text field and a button (that returns to the first view controller). Here is the code for the first view controller:
#IBOutlet weak var labelText: UILabel!
func userDidEnterInformation(info: String) {
labelText.text = info;
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "transition"){
let secondVC: SecondViewController = segue.destinationViewController as! SecondViewController;
secondVC.delegate = self;
}
}
Here's the code for the second view controller:
protocol DataEnteredDelegate{
func userDidEnterInformation(info: String);
}
#IBOutlet weak var userText: UITextField!
var delegate: DataEnteredDelegate? = nil;
#IBAction func buttonPressed(sender: AnyObject) {
let information = userText.text!;
delegate!.userDidEnterInformation(information);
self.navigationController?.popToRootViewControllerAnimated(true);
}
My understanding is that in the text inside the text field gets stored in the information constant, then the userDidEnterInformation method from the protocol is called, with the method being defined inside the first view controller. This method then changes the label inside the first view controller. The thing is, I'm not sure what is happening in the prepareForSegue function. Specifically, I'm not sure what's the purpose of secondVC.delegate = self.
I would appreciate any sort of clarity on delegation.
The diagram is simple but can help you understand what's going on.
FirstViewController must conform to the DataEnteredDelegate protocol you have defined (see Sumit's answer). When using secondVC.delegate = self, you are saying that for the segue transition with the destination being a SecondViewController, the attribute delegate of that SecondViewController instance will be set to this instance of FirstViewController, thus delegating things from SecondViewController to your FirstViewController as made possible by the DataEnteredDelegate protocol.
The protocol you created in second viewcontroller is an Interface. You must implement your first view controller with the DataEnteredDelegate protocol.
class FirstViewController:UIViewController, DataEnteredDelegate{
func userDidEnterInformation(info: String) {
//stub
}
}
If the delegate of the second VC is not set in prepareForSegue() it remains nil. The second VC is then unable to call the first VC.
On a side note, if the delegate is nil your code will crash because delegate! is trying to unwrap an optional binding with the value of nil. It's better to first unwrap the delegate variable:
if let handler = delegate {
handler.userDidEnterInformation(information)
}
Alternatively, you could use Swift's Optional Chaining, calling userDidEnterInformation only if delegate is not nil.
delegate?.userDidEnterInformation(information);
In addition it is recommended to declare the delegate weak, to prevent retain cycles:
weak var delegate: DataEnteredDelegate?
Delegates and Protocols
Do not try to figure out how the dictionary definition of “delegate” fits with the concept of delegation in Swift. It doesn't.
Delegation in Swift is an agreement between two players—a sensing object and a requesting object. The “delegate” is the “requesting object.” Just think “asker” or “requester” every time you see “delegate” and it will make a lot more sense. Here is their agreement...
The Sensing Object (Second View Controller):
I have data from some event that took place. I will publish instructions (a protocol) on how you may access that data. If you want it, you must do three things.
You must declare in your class type that your class abides by my protocol.
You must write the functions that I describe in my protocol. I don't care what those functions do but the function type must match what I publish.
In YOUR code, you must set MY “delegate” (think “asker”) property to point to you. {secondVC.delegate = self} That way I can call one of YOUR functions to deliver the data.
After that, when I get some data, I will call one of the functions in your object that I told you to write. My call to your function will contain the data you are looking for as one of the arguments. {delegate!.userDidEnterInformation(information)} Note: delegate! (asker!) is YOU.
The Delegate (Requesting) Object (First View Controller):
O.K. You've got a deal.