расширение UIAlertController удобство инициализации предупреждение

Когда я определяю удобный инициализатор UIAlertController:

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

и использовать его в действии кнопки в моем подклассе UIViewController:

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

и нажмите эту кнопку в симуляторе, я получаю предупреждение:

Попытка загрузить представление контроллера представления во время его освобождения не допускается и может привести к неопределенному поведению (UIAlertController).

Однако я не получаю предупреждения, если вместо удобного инициализатора я использую глобальную функцию:

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
}

Я сообщил об этом в Apple как об ошибке iOS SDK.

Пока это не будет исправлено, можно ли игнорировать предупреждение и использовать удобный инициализатор?


person ma11hew28    schedule 19.11.2015    source источник


Ответы (2)


Я заметил ту же проблему с удобными инициализаторами. На самом деле это два бага сразу.

  1. Swift выделяет экземпляр UIAlertController.
  2. Swift для вашего удобства вызывает init с экземпляром, созданным Swift.
  3. Там вы вызываете удобный init UIKit, который на самом деле является фабричным методом Objective-C +(id) alertControllerWithTitle:message:preferredStyle:.
  4. Там UIKit выделяет свой собственный экземпляр UIAlertController. (Ошибка №1)
  5. UIKit устанавливает свой собственный экземпляр.
  6. UIKit освобождает ваш экземпляр Swift.
  7. deinit (dealloc) UIAlertController обращается к свойству view, что приводит к сообщению журнала. (Ошибка №2)
  8. Управление возвращается к вашему собственному удобству, когда self молча меняется с экземпляра Swift UIAlertController на экземпляр UIKit.
  9. Все, что вы сейчас делаете, происходит в экземпляре, созданном UIKit, и это нормально.

Итак, первая ошибка заключается в том, что Swift создает временный UIAlertController, который уничтожается без использования.

Вторая ошибка заключается в том, что UIViewController обращается к свойству view во время деинициализации, чего не должно быть.


Относительно вашего вопроса:
Обе ошибки не должны вызывать проблем, поэтому пока мы можем просто проигнорировать это предупреждение. Я тоже так делаю и пока проблем не было - только это предупреждение в логе.

person fluidsonic    schedule 26.11.2015
comment
Ух ты. Отличные навыки отладки! Откуда ты все это знаешь? Вы только что использовали отладчик Xcode, установили точку останова и выполнили шаг? - person ma11hew28; 29.11.2015
comment
Сочетание опыта, знания среды выполнения Objective-C и да, отладки :) - person fluidsonic; 29.11.2015

Я также столкнулся с той же проблемой

Попытка загрузить представление контроллера представления во время его освобождения не допускается и может привести к неопределенному поведению (UIAlertController).

Поэтому я перешел на альтернативный способ для этого.

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

вызовите этот метод в своем ViewController как

showAlertforNetworkFailure("Server Error!!!", alertmessage: "Server does not responding,please Try in later.", ButtonTitle: "Okay", viewController: self)
person Anbu.Karthik    schedule 19.11.2015
comment
Интересный ответ. Спасибо. :-) Я предпочитаю свой удобный инициализатор, поскольку он содержит меньше кода и предоставляет обработчик completion. - person ma11hew28; 19.11.2015
comment
Вы должны иметь возможность просто вызывать presentViewController, не беспокоясь о том, является ли он контроллером верхнего представления или нет, из документации UIViewController: объект, для которого вы вызываете этот метод, не всегда может быть тем, который обрабатывает презентацию. Каждый стиль презентации имеет свои правила, регулирующие его поведение. Например, полноэкранная презентация должна быть сделана контроллером представления... Если текущий контроллер представления не может выполнить запрос, он перенаправляет запрос вверх по иерархии контроллера представления своему ближайшему родителю. - person Oscar Gomez; 06.04.2016