2016-01-29 17 views
6

Tôi đang cố gắng viết một loạt các hàm chung sẽ sắp xếp thông qua một chồng viewControllers bằng cách chuyển một lớp hoặc lớp con Loại UIViewController và sau đó trả về cá thể viewController "found" hoặc nil. Cho đến nay tôi đã không thể thậm chí có đoạn đơn giản này để biên dịch:Swift Generics: chức năng với T.Type như tham số trả về tùy chọn T

extension UINavigationController { 

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

và sẽ gọi:

navController.fhk_find(fooViewController.self) 

Tuy nhiên, trình biên dịch được nói với tôi rằng viewControllerType không phải là một loại.

Tôi không chắc chắn chính xác những gì tôi đang mất tích ở đây ...

Trả lời

4

Bạn cần phải cast viewControllers.first (trong trường hợp nó tồn tại) để T, chứ không phải là tham số viewControllerType. Trong thực tế, bạn sẽ không cần sử dụng tham số này chút nào; bạn có thể sửa đổi tiện ích mở rộng của mình thành các mục sau:

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

Ví dụ sử dụng sau đây. Lưu ý rằng bạn gọi với số fooViewController.dynamicType thay vì fooViewController.self. Loại sau cung cấp cho bạn giá trị của fooViewController.self thay vì loại, tức là, fooViewController.self = fooViewController.self. Tuy nhiên, lưu ý rằng một chuyển đổi cố gắng từ một loại phân lớp đến siêu lớp của nó sẽ luôn thành công, vì vậy giải pháp trên sẽ xác định chính xác các cá thể lớp con (các lớp con của UIViewController), trong khi nếu bạn truy vấn một cá thể siêu lớp (ví dụ: T là siêu lớp UIViewController) thì viewController = viewController as? T sẽ luôn thành công ngay cả khi viewController thực tế là một phiên bản của lớp con của UIViewController.


Sử dụng fhkFindFirst(...) để xác định trường hợp lớp con: OK

Trong ví dụ hai trường lớp con sau đây được xác định một cách chính xác và tài liệu tham khảo của họ trở lại gọi.

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

Sử dụng fhkFindFirst(...) để tìm trường lớp cha: không như dự định

Trong khi trong trường hợp sau đây, fooViewController là một đối tượng UIViewController, và là mis-xác định là một BarViewController, vì loại chuyển đổi của đối tượng BarViewController (trong UINavigationController.viewControllers) đến UIViewController thành công.

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

Để kết thúc; ở trên sẽ làm việc miễn là bạn chỉ sử dụng phương pháp để tìm kiếm các lớp con của UIViewController; trong khi nếu bạn cố gắng tìm kiếm một đối tượng siêu lớp, bạn sẽ nhận được bộ điều khiển đầu tiên trong UINavigationController.viewControllers.


Addition liên quan đến edelaney05 với: là câu hỏi có liên quan trong ý kiến ​​dưới đây

Tôi sẽ bắt đầu bằng cách trích dẫn các câu hỏi trong trường hợp nhận xét đã bị xóa:

edelaney05 : Có cách nào để bảo vệ chống lại điều này không? Đây là một trường hợp khá thú vị để xem xét đặc biệt nếu bạn có một lớp trung gian . class FooVC: AwesomeVC { ... }, class BarVC: AwesomeVC { ... }class AwesomeVC: UIViewController { ... }.

, bạn có thể làm việc xung quanh hành vi này, trong trường hợp bạn biết bạn sẽ được làm việc với khác hơn là chỉ thuần túy (1st cấp) lớp con của UIViewController; ví dụ: để cho phép tìm kiếm trường hợp đầu tiên của

  • Các lớp con tới UIViewController. (+)
  • Phân lớp cho các lớp con này. (++)

Chúng tôi có thể sử dụng so sánh kiểu động để đảm bảo rằng chúng tôi không thực hiện chuyển đổi một thể hiện phân lớp thành siêu lớp của nó (và do đó xác định sai trường hợp lớp con cho).

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! Tôi biết nó sẽ là một cái gì đó đơn giản. Cảm ơn bạn! Và cảm ơn vì đã cứu tôi đau đầu trong tương lai bằng cách chỉ ra .self vs .dynamicType. –

+0

@StephenNewton Rất vui được trợ giúp. Cũng lưu ý rằng tôi đã cập nhật cho bạn phương thức mở rộng để thực hiện theo quy ước đặt tên Swift 'camelCase' cho tên phương thức. – dfri

+2

@StephenNewton Lưu ý chỉnh sửa của tôi: giải pháp ở trên hoạt động tốt miễn là bạn chỉ tìm kiếm các trường hợp lớp con của 'UIViewController', nhưng sẽ luôn trả về trường hợp đầu tiên trong' UINavigationController.viewControllers' nếu bạn cố gắng tìm kiếm đối tượng superclass ('UIViewController '). Điều này nên được OK, tuy nhiên, như tất cả các bạn xemcontrollers nên được trường hợp của các lớp con của 'UIViewController'. – dfri

Các vấn đề liên quan