Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

extension UIAlertController convenience init warning

When I define a UIAlertController convenience initializer:

extension UIAlertController {
    convenience init(message: String?) {
        self.init(title: nil, message: message, preferredStyle: .Alert)
        self.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
    }
}

and use it in a button action in my subclass of UIViewController:

func buttonAction(button: UIButton) {
    let alert = UIAlertController(dictionary: nil, error: nil, handler: nil)
    presentViewController(alert, animated: true, completion: nil)
}

and click that button on the Simulator, I get a warning:

Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (UIAlertController)

However, I don't get a warning if instead of a convenience initializer, I use a global function:

func UIAlertControllerWithDictionary(message: String?) -> UIAlertController {
    let alert = UIAlertController(title: nil, message: message, preferredStyle: .Alert)
    alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
    return alert
}

I've reported this to Apple as an iOS SDK bug.

Until it's fixed, is it OK to ignore the warning and use the convenience initializer?

like image 939
ma11hew28 Avatar asked Nov 19 '15 13:11

ma11hew28


2 Answers

I noticed the same issue with convenience initializers. It's actually two bugs at once.

  1. Swift allocates a UIAlertController instance.
  2. Swift calls your convenience init with the instance created by Swift.
  3. There you call UIKit's convenience init, which is actually the Objective-C factory method +(id) alertControllerWithTitle:message:preferredStyle:.
  4. There UIKit allocates its own UIAlertController instance. (Bug #1)
  5. UIKit sets up its own instance.
  6. UIKit deallocates your Swift instance.
  7. UIAlertController's deinit (dealloc) accesses the view property which leads to the log message. (Bug #2)
  8. Control comes back to your own convenience init where self silently changed from Swift's UIAlertController instance to the one from UIKit.
  9. Everything you do now happens on the instance created by UIKit which is fine.

So the first bug is that Swift creates a temporary UIAlertController which is destroyed without ever being used.

The second bug is that UIViewController accesses the view property during deinitialization which it shouldn't.


Regarding your question:
Both bugs shouldn't be problematic so we can simply ignore the warning for now. I do that too and there weren't any problems yet - just that warning in the log.

like image 121
fluidsonic Avatar answered Sep 28 '22 14:09

fluidsonic


I also faced the same issue

Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (UIAlertController)

So I moved to the alternate way for this.

 import UIKit
import Foundation

//the show alert function for failure
func showAlertforNetworkFailure(alerttitle :String, alertmessage: String,ButtonTitle: String, viewController: UIViewController)
{


    let alertController = UIAlertController(title: alerttitle, message: alertmessage, preferredStyle: .Alert)
    let okButtonOnAlertAction = UIAlertAction(title: ButtonTitle, style: .Default)
        { (action) -> Void in
            //what happens when "ok" is pressed

    }
    alertController.addAction(okButtonOnAlertAction)
    alertController.show()



}

// function for show alert in Main View Controller
extension UIAlertController {

    func show() {
        present(true, completion: nil)
    }

    func present(animated: Bool, completion: (() -> Void)?) {
        if let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController {
            presentFromController(rootVC, animated: animated, completion: completion)
        }
    }

    private func presentFromController(controller: UIViewController, animated: Bool, completion: (() -> Void)?) {
        if let navVC = controller as? UINavigationController,
            let visibleVC = navVC.visibleViewController {
                presentFromController(visibleVC, animated: animated, completion: completion)
        }  else {
                controller.presentViewController(self, animated: animated, completion: completion);
        }
    }
}

call this method in your ViewController as

showAlertforNetworkFailure("Server Error!!!", alertmessage: "Server does not responding,please Try in later.", ButtonTitle: "Okay", viewController: self)
like image 35
Anbu.Karthik Avatar answered Sep 28 '22 14:09

Anbu.Karthik