2016-01-29 11 views
6

Próbuję napisać serię ogólnych funkcji, które będą sortować za pomocą stosu viewControllerów, przepuszczając klasę lub podklasę klasy UIViewController, a następnie zwracając instancję "found" viewController lub zero. Do tej pory nie udało mi się nawet dostać ten prosty fragment skompilować:Swift Generics: funkcja z T.Type jako parametr zwraca opcjonalnie T

extension UINavigationController { 

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

i nazwałbym:

navController.fhk_find(fooViewController.self) 

Jednak kompilator mówi mi, że viewControllerType nie jest typem.

Nie jestem dokładnie pewien, czego mi brakuje tutaj ...

Odpowiedz

4

Musisz rzucać viewControllers.first (w przypadku, gdy istnieją) do T, zamiast parametru viewControllerType. W rzeczywistości nie będziesz musiał w ogóle korzystać z tego parametru; możesz zmodyfikować swoje rozszerzenie na następujące:

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

Przykładowe zastosowanie przedstawiono poniżej. Pamiętaj, że dzwonisz pod numerem fooViewController.dynamicType zamiast fooViewController.self. Ta ostatnia podaje wartość o wartości z fooViewController.self zamiast typu, tj. fooViewController.self = fooViewController.self.

Należy jednak zauważyć, że próba konwersji z klasy podklasy do jej nadklasy zawsze zakończy się sukcesem, więc powyższe rozwiązanie poprawnie rozpozna instancje podklasy (podklasy UIViewController), natomiast w przypadku zapytania o wystąpienie nadklasy (np. T jako nadklasa UIViewController) wtedy viewController = viewController as? T zawsze się powiedzie, nawet jeśli viewController jest w rzeczywistości instancją podklasy UIViewController.


Korzystanie fhkFindFirst(...) zidentyfikowanie instancji podklasy: Ok

w poniższym przykładzie dwóch przypadkach podklasy są prawidłowo zidentyfikowane i ich odniesienia powrócił do rozmówcy.

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> */ 

Korzystanie fhkFindFirst(...) znaleźć nadklasy instancje: nie zgodnie z przeznaczeniem

Natomiast w następującym przypadku, fooViewController jest obiektem UIViewController i jest błędnie zidentyfikowany jako BarViewController, od typu konwersji z obiektu BarViewController (w UINavigationController.viewControllers) na UIViewController powiodło się.

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> */ 

do zawijania; powyższe będzie działać tak długo, jak tylko użyjesz tej metody do wyszukiwania podklas o numerze UIViewController; natomiast jeśli spróbujesz wyszukać obiekt nadklasy, otrzymasz pierwszy kontroler w UINavigationController.viewControllers.


Dodawanie w zakresie edelaney05: s powiązane pytanie w komentarzach poniżej

zacznę cytując pytanie w przypadku komentarza miały zostać usunięte:

edelaney05 : Czy istnieje sposób, aby się przed tym uchronić? Jest to bardzo ciekawy przypadek, który należy wziąć pod uwagę, szczególnie jeśli masz klasę pośrednią . class FooVC: AwesomeVC { ... }, class BarVC: AwesomeVC { ... } i class AwesomeVC: UIViewController { ... }.

Tak, można obejść ten problem, w przypadku znasz będziesz pracował z innymi niż tylko czystego (1 poziomie) podklasy UIViewController; powiedzmy, aby umożliwić znalezienie pierwszego wystąpienia podrzędnego:

  • Podklasy do UIViewController. (+)
  • Podklasy do tych podklas. (++)

Możemy skorzystać z dynamicznego porównywania typów, aby upewnić się, że nie wykonujemy konwersji instancji podklasy do jej nadklasy (i dlatego błędnie identyfikujemy instancję podklasy jako instancję nadklasy, której szukamy dla).

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! Wiedziałem, że to będzie coś prostego. Dziękuję Ci! I dzięki za uratowanie mi przyszłego bólu głowy przez wskazanie .self vs .dynamicType. –

+0

@StephenNewton Z przyjemnością pomożemy. Zwróć też uwagę, że zaktualizowałem metodę rozszerzenia, aby zastosować konwencję nazewnictwa Swift 'camelCase' dla nazw metod. – dfri

+2

@StephenNewton Notatka moja edycja: powyższe rozwiązanie działa dobrze, dopóki wyszukiwane są instancje podklasy 'UIViewController', ale zawsze zwraca pierwszą instancję w' UINavigationController.viewControllers', jeśli spróbujesz wyszukać obiekt nadklasy ('UIViewController '). To powinno być jednak w porządku, ponieważ wszystkie kontrolery viewinner powinny być instancjami podklas obiektu 'UIViewController'. – dfri

Powiązane problemy