2016-01-29 14 views
6

Sto tentando di scrivere una serie di funzioni generiche che selezioneranno una pila di viewControllers passando una classe UIViewController o una classe sottoclasse e quindi restituiranno l'istanza di il "found" viewController o nil. Finora sono stato in grado di ottenere anche questo semplice frammento di codice per la compilazione:Swift Generics: funzione con T.Type come parametro restituisce opzionale T

extension UINavigationController { 

    func fhk_find<T: UIViewController>(viewControllerType: T.Type) -> T? 
    { 
     if let viewController = viewControllers.first as? viewControllerType { 
      return viewController 
     } 
     else { 
      return nil 
     } 
    } 
} 

e chiamerei:

navController.fhk_find(fooViewController.self) 

Tuttavia, il compilatore mi sta dicendo che viewControllerType non è un tipo.

Non sono esattamente sicuro di cosa mi manca qui ...

risposta

4

È necessario lanciare l'viewControllers.first (nel caso in cui esista) per T, piuttosto che al parametro viewControllerType. Di fatto, non è necessario utilizzare questo parametro; è possibile modificare la propria estensione al seguente:

extension UINavigationController { 
    func fhkFindFirst<T: UIViewController>(_: T.Type) -> T? { 
     for viewController in viewControllers { 
      if let viewController = viewController as? T { 
       return viewController 
      } 
     } 
     return nil 
    } 
} 

L'utilizzo di esempio segue di seguito. Si noti che si chiama con fooViewController.dynamicType anziché fooViewController.self. Quest'ultimo ti dà il valore anziché il tipo, , ovvero fooViewController.self = fooViewController.self.

Ora Si noti, tuttavia, che un tentativo di conversione da un tipo sottoclasse alla sua superclasse avrà sempre successo, per cui la soluzione di cui sopra sarà identificare correttamente le istanze delle sottoclassi (sottoclassi di UIViewController), mentre se la tua ricerca per un'istanza superclasse (vale a dire, T come superclasse UIViewController) quindi viewController = viewController as? T avrà sempre successo anche se viewController è in realtà un'istanza di una sottoclasse di UIViewController.


Utilizzando fhkFindFirst(...) per identificare le istanze delle sottoclassi: OK

Nell'esempio seguente due istanze della sottoclasse siano correttamente identificati e loro riferimenti restituito al chiamante.

class FooViewController : UIViewController { } 
class BarViewController : UIViewController { } 

let fooViewController = FooViewController() 
let barViewController = BarViewController() 

let navController = UINavigationController(rootViewController: fooViewController) 
navController.addChildViewController(barViewController) 

print(navController.fhkFindFirst(fooViewController.dynamicType) ?? "None found.") 
// or: print(navController.fhkFindFirst(FooViewController)) 
/* <__lldb_expr_1582.FooViewController: 0x7fb97840ad80> */ 

print(navController.fhkFindFirst(barViewController.dynamicType) ?? "None found.") 
// or: print(navController.fhkFindFirst(BarViewController)) 
/* <__lldb_expr_1582.BarViewController: 0x7fb978709340> */ 

Utilizzando fhkFindFirst(...) per trovare le istanze della superclasse: non come previsto

Mentre nel caso seguente, fooViewController è un oggetto UIViewController, ed è mis-identificato come BarViewController, dal momento che la conversione del tipo dell'oggetto BarViewController (in UINavigationController.viewControllers) a UIViewController ha esito positivo.

class BarViewController : UIViewController { } 

let fooViewController = UIViewController() 
let barViewController = BarViewController() 

let navController = UINavigationController(rootViewController: barViewController) 
navController.addChildViewController(fooViewController) 

print(navController.fhkFindFirst(fooViewController.dynamicType) ?? "None found.") 
// or: print(navController.fhkFindFirst(UIViewController)) 
    /* <__lldb_expr_1533.BarViewController: 0x7fa519e2bd40> <-- "wrong" one */ 

print(navController.fhkFindFirst(barViewController.dynamicType) ?? "None found.") 
// or: print(navController.fhkFindFirst(BarViewController)) 
    /* <__lldb_expr_1533.BarViewController: 0x7fa519e2bd40> */ 

Per concludere; quanto sopra funzionerà finché si utilizza il metodo solo per cercare sottoclassi di UIViewController; mentre se si tenta di cercare un oggetto superclasse, si otterrà il primo controller in UINavigationController.viewControllers.


aggiunta per quanto riguarda edelaney05: s domanda relativa nei commenti qui sotto

inizierò citando la questione nel caso in cui il commento dovesse essere cancellato:

edelaney05 : C'è un modo per proteggersi da questo? Questo è un caso davvero interessante da considerare soprattutto se hai una classe intermedia . class FooVC: AwesomeVC { ... }, class BarVC: AwesomeVC { ... } e class AwesomeVC: UIViewController { ... }.

, è possibile aggirare il problema, nel caso in cui sai che lavorerete con diverso solo puro (1 ° livello) sottoclassi di UIViewController; per consentire di trovare la prima istanza di

  • Sottoclassi a UIViewController. (+)
  • Sottoclassi a queste sottoclassi. (++)

Possiamo fare uso di confronto di tipo dinamico al fine di garantire che non eseguire una conversione di un'istanza di sottoclasse alla sua superclasse (e quindi misidentifying un'istanza di sottoclasse come istanza della superclasse che stiamo cercando per).

extension UINavigationController { 
    func fhkFindFirst<T: UIViewController>(_: T.Type) -> T? { 
     for viewController in viewControllers { 
      if let supClassType = viewController.superclass?.dynamicType where supClassType != T.Type.self { 
       if let viewController = viewController as? T { 
        return viewController 
       } 
      } 
     } 
     return nil 
    } 
} 

class FooBarViewController: UIViewController { } 

class FooViewController : FooBarViewController { } 
class BarViewController : FooBarViewController { } 

let fooBarViewController = FooBarViewController() 
let fooViewController = FooViewController() 
let barViewController = BarViewController() 

let navController = UINavigationController(rootViewController: fooViewController) 
navController.addChildViewController(barViewController) 
navController.addChildViewController(fooBarViewController) 

print(navController.fhkFindFirst(FooViewController) ?? "None found.") 
/* <__lldb_expr_1582.FooViewController: 0x7fe22a712e40> */ 

print(navController.fhkFindFirst(BarViewController) ?? "None found.") 
/* <__lldb_expr_1582.BarViewController: 0x7fe22a4196a0> */ 

print(navController.fhkFindFirst(FooBarViewController) ?? "None found.") 
/* <__lldb_expr_1582.FooBarViewController: 0x7fe22a70ee60> */ 
+0

Ah! Sapevo che sarebbe stato qualcosa di semplice. Grazie! E grazie per avermi salvato il mal di testa futuro sottolineando .self vs .dynamicType. –

+0

@StephenNewton Felice di aiutare. Nota inoltre che ho aggiornato il tuo metodo di estensione per seguire la convenzione di denominazione Swift 'camelCase' per i nomi dei metodi. – dfri

+2

@StephenNewton Nota la mia modifica: la soluzione sopra funziona bene finché si cercano solo istanze di sottoclasse di 'UIViewController', ma si tornerà sempre alla prima istanza in' UINavigationController.viewControllers' se si tenta di cercare un oggetto superclasse ('UIViewController '). Questo dovrebbe essere OK, tuttavia, poiché tutti i viewcontrollers dovrebbero essere istanze di sottoclassi di 'UIViewController'. – dfri

Problemi correlati