2015-11-19 16 views
6

Quando mi definisco un inizializzatore UIAlertController convenienza:estensione UIAlertController convenienza init avvertimento

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

e utilizzarlo in un pulsante di azione nel mio sottoclasse di UIViewController:

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

e scatto che pulsante sul simulatore , Ricevo un avviso:

Tentativo di caricare la vista di un controller di vista whi Le si deallocando non è consentito e può causare un comportamento indefinito (UIAlertController)

Tuttavia, non ottengo un avviso se invece di un inizializzatore convenienza, io uso una funzione globale:

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 
} 

Ho segnalato questo a Apple come un bug di iOS SDK.

Fino a quando non viene risolto, è OK ignorare l'avviso e utilizzare l'inizializzatore di convenienza?

risposta

10

ho notato lo stesso problema con inizializzatori di convenienza. In realtà sono due bug contemporaneamente.

  1. Swift assegna un'istanza UIAlertController.
  2. Swift chiama il tuo init di comodità con l'istanza creata da Swift.
  3. Lì si chiama init di convenienza di UIKit, che in realtà è il metodo factory Objective-C +(id) alertControllerWithTitle:message:preferredStyle:.
  4. Qui UIKit alloca la propria istanza UIAlertController. (Bug # 1)
  5. UIKit imposta la propria istanza.
  6. UIKit rilascia l'istanza Swift.
  7. UIAlertController 's deinit (dealloc) accede alla proprietà view che conduce al messaggio di registro. (Bug # 2)
  8. Il controllo ritorna al proprio init init, dove self è stato modificato silenziosamente dall'istanza di Swift UIAlertController a quella di UIKit.
  9. Tutto ciò che fai ora accade sull'istanza creata da UIKit che va bene.

Quindi il primo bug è che Swift crea uno UIAlertController temporaneo che viene distrutto senza mai essere utilizzato.

Il secondo bug è che UIViewController accede alla proprietà view durante la deinizzazione che non dovrebbe.


quanto riguarda la tua domanda:
Entrambi i bug non dovrebbe essere problematico in modo che possiamo semplicemente ignorare l'avviso per il momento. Lo faccio anche io e non c'erano ancora problemi - solo quell'avvertimento nel registro.

+0

Wow. Eccellenti capacità di debugging! Come fai a sapere tutto questo? Hai appena usato il debugger Xcode, hai impostato un breakpoint e sei passato? – ma11hew28

+1

Un mix di esperienza, conoscenza del runtime Objective-C e sì, debugging :) – fluidsonic

3

Ho anche affrontato lo stesso problema

tentativo di caricare la visualizzazione di un controller della vista durante la deallocazione non è consentito e può causare il comportamento indefinito (UIAlertController)

Così spostato al modo alternativo per questo.

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

chiamata di questo metodo nella ViewController come

showAlertforNetworkFailure("Server Error!!!", alertmessage: "Server does not responding,please Try in later.", ButtonTitle: "Okay", viewController: self) 
+0

Risposta interessante. Grazie. :-) Preferisco il mio inizializzatore di convenienza dal momento che è meno codice ed espone il gestore 'completamento'. – ma11hew28

+0

@MattDiPasquale - tanx molto più vecchio .... –

+0

Dovresti essere in grado di chiamare semplicemente presentViewController senza doversi preoccupare se è il controller di visualizzazione superiore o meno, dalla documentazione di UIViewController: l'oggetto su cui si chiama questo metodo potrebbe non essere sempre quello che gestisce la presentazione. Ogni stile di presentazione ha regole diverse che regolano il suo comportamento. Ad esempio, una presentazione a schermo intero deve essere eseguita da un controller di visualizzazione ..... Se il controller di visualizzazione corrente non è in grado di soddisfare una richiesta, inoltra la richiesta sulla gerarchia del controller di visualizzazione a quella principale –