AbaEesa
AbaEesa

Reputation: 998

Not able to access variable present in class from handler

import UIKit

class ViewController: UIViewController
{

    var icnNum : Int64 = 0

    let stopHandler =
        {
            (action:UIAlertAction!) -> Void in

            let num = icnNum
    }


    func showAlert( userStatus: String )
    {

        let alert = UIAlertController(title: "", message: "", preferredStyle: .alert)

        alert.title = "What you want to do?"

        alert.addAction(UIAlertAction(title: "Stop", style: .default, handler: stopHandler))

    }



}

I don't know how to access that icnNum from the handler. Am getting following error. I know that I can't access that variable directly but what is the way.

Instance member 'icnNum' cannot be used on type 'ViewController'

Upvotes: 4

Views: 295

Answers (3)

Jeffery Thomas
Jeffery Thomas

Reputation: 42588

You can pass a method as the handler. This avoids needing to have a bare closure floating around.

class ViewController: UIViewController {

    var icnNum : Int64 = 0

    func stopHandler(_ action: UIAlertAction) {
        let num = icnNum
    }

    func showAlert(userStatus: String) {
        let alert = UIAlertController(title: "", message: "", preferredStyle: .alert)

        alert.title = "What you want to do?"

        alert.addAction(UIAlertAction(title: "Stop", style: .default, handler: stopHandler))
    }

}

UPDATE

The expected behavior for handlers like this is to use a closure which can include the local context.

func showAlert(userStatus: String) {
    let localContext = "Local information"
    // …
    alert.addAction(UIAlertAction(title: "Stop", style: .default) { action in
        if localContext == "Local information" {
           // Do Something
        }
    })
}

However, you don't have a local context. The context you are working from is contained with in the object instance.

func showAlert(userStatus: String) {
    // …
    alert.addAction(UIAlertAction(title: "Stop", style: .default, handler: stopHandler))
}

Because you don't have anything local used in your handler, you don't need a closure.

func stopHandler(_ action: UIAlertAction) {
    let num = icnNum
}

will do the same thing as the closure handler, but gives you the context of the object instance.

Upvotes: 1

florieger
florieger

Reputation: 1390

Define the stopHandler closure inside the showAlert() function and it should work.

class ViewController: UIViewController
{
    var icnNum : Int64 = 0

        func showAlert( userStatus: String ) {
            let stopHandler = { (action:UIAlertAction!) -> Void in
                let num = self.icnNum
            }

            let alert = UIAlertController(title: "", message: "", preferredStyle: .Alert)
            alert.title = "What you want to do?"

            alert.addAction(UIAlertAction(title: "Stop", style: .Default, handler: stopHandler))
        }   
    }
}

The compiler will force you to write self.icnNum instead of icnNum to make it obvious that the closure will hold a reference to self.

Storing the stopHandler closure as a variable, like you did in your example, would create a cyclic reference. Your ViewController instance holds a strong reference to the stopHandler closure, the closure holds a strong reference to self (which is a pointer to you ViewController instance).

Update if you want to reuse the stopHandler

class ViewController: UIViewController {      
    var icnNum : Int64 = 0
    var stopHandler: ((action:UIAlertAction!) -> Void)?

    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
        self.stopHandler = { [weak self] (action:UIAlertAction!) -> Void in
            let num = self?.icnNum
        }
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }


    func showAlert( userStatus: String )
    {
        let alert = UIAlertController(title: "", message: "", preferredStyle: .Alert)
        alert.title = "What you want to do?"

        alert.addAction(UIAlertAction(title: "Stop", style: .Default, handler: stopHandler))      
    }
}

Notice the [weak self] when setting the stopHandler closure. This will prevent the closure from keeping a strong reference to self and avoid the cyclic reference described above.

More details at: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html#//apple_ref/doc/uid/TP40014097-CH20-ID57

Upvotes: 5

jignesh Vadadoriya
jignesh Vadadoriya

Reputation: 3310

you can write your closer like this way

 let stopHandler = {
        (icnNum: Int64 ,action:UIAlertAction!) -> Void in

        let num = icnNum
}

while calling this closer like this way

alert.addAction(UIAlertAction(title: "Stop", style: .default, handler: stopHandler(self.icnNum, UIAlertAction!)))

Upvotes: 2

Related Questions