2016-11-30 63 views
0

努力實現像在迅速 UIViewController<Routable>以下目標C代碼,但抱怨說,它不能推斷泛型類型實現協議的類型的swift方法參數?

我覺得要我以某種方式確定使用什麼子類的UIViewController,但我不不在乎。我只是想實現可路由

public protocol Routable { 
    static func provideInstance(id: String?) -> Self? 
} 

public class Router { 
    public func getDestinationViewController<T: UIViewController>(url: URL) -> T? where T: Routable { 
     return nil 
    } 
} 

// Generic parameter `T` could not be inferred 
let vc = Router().getDestinationViewController(url: URL(string: "www.domain.com/users/1")!) 

我知道我可以使用鑄造來解決問題,只是有方法返回一個的UIViewController或可路由的一個UIViewController,但我寧願做正確的方式

回答

3

在這種情況下,您認爲什麼樣的精確類型vc? (我的意思是在編譯時確定類型,查看上面的代碼,而不是「某些我們直到運行時才知道的類型」)。如果你不能回答這個問題,編譯器也不能。

這裏的正確方法是「返回UIViewController或Routable」,就像您說的那樣。這就是你現在知道的一切。試圖說更精確的東西將是不正確的。

vc是類型推斷的事實只是一個方便。它在編譯時仍然必須具有特定的已知類型。如果你不能在源代碼中編寫它,編譯器不能推斷它。

+0

是否真的不可能返回也是可路由的UIViewController?這在objc中是可能的 - (UIViewController * )gimeeSomething' – aryaxt

+1

在Swift中無法表達這一點。你將不得不在UIViewController中添加你想要的特定方法到Routable協議上。這是一個衆所周知的限制,在未來的Swift版本中可能會得到改進,但目前是不可能的。 –

1

在使用泛型函數時,通用參數的類型可以根據您分配結果的變量的類型(或者在返回Void的函數的情況下,根據您傳遞給參數的參數的具體類型類型T)。

在你的例子中,你不告訴編譯器你的vc變量是什麼類型,所以你會得到一個錯誤。你可以像這樣很容易解決這個問題:

class MyViewController: UIViewController, Routable { 
    public static func provideInstance(id: String?) -> Self? { 
     return self.init() 
    } 
} 

// give `vc` a concrete type so `T` can be inferred: 
let vc: MyViewController? = Router().getDestinationViewController(url: URL(string: "www.domain.com/users/1")!) 

編輯:

由於您的問題似乎是一起的「我怎麼複製的UIViewController *<Routable> Objective-C的概念」線以上,這你不能在Swift中做,我會提到你可能會覺得值得回顧一下你的設計。當我從Objective-C移動到Swift時,我認爲無法使用類似UIViewController *<Routable>這樣的東西很難解決(甚至向Apple提交了一個關於它的錯誤報告),但實際上它並不是一個問題。

您的Router類需要一些信息在您的視圖控制器能夠正確路由。在你的例子中,你正在尋找一個與特定URL關聯的視圖控制器。言下之意是,你UIViewController財產包含此URL,因此而不是返回一個Routable協議,正確的做法是子類的UIViewController像這樣:

class RoutableViewController: UIViewController { 
    let url: URL = URL(string: "...") 
} 

現在你的路由器看起來像:

class Router { 
    var routables = [RoutableViewController]() 

    func viewControllerWithURL(_ url: URL) -> RoutableViewController? { 
     return routables.first 
    } 
} 

現在你不需要做任何類型轉換,並且(明顯)保持類型安全。

編輯2:

這裏有一個解決方案,使每鑑於控制器符合Routable(我不認爲你獲得多少與我在以前的編輯提出的解決方案,但在這裏它是無論如何) :

protocol Routable { 
    var url: URL? { get } 
} 

// now every UIViewController conforms to Routable 
extension UIViewController: Routable { 
    var url: URL? { return nil } 
} 

class MyViewController: UIViewController { 
    private let myURL: URL 

    override var url: URL? { return myURL } 

    init(url: URL) { 
     myURL = url 
     super.init(nibName: nil, bundle: nil) 
    } 

    required init?(coder aDecoder: NSCoder) { 
     fatalError("init(coder:) has not been implemented") 
    } 
} 

class Router { 
    var viewControllers: [UIViewController] = [ 
     MyViewController(url: URL(string: "foo")!), 
     UIViewController() 
    ] 

    func viewController(for url: URL) -> UIViewController? { 
     for viewController in viewControllers { 
      if viewController.url == url { return viewController } 
     } 

     return nil 
    } 
} 
+0

這不起作用,因爲我不知道什麼vc我會回來,我希望它是動態的。路由器通過我的註冊路由並找到一個匹配,並返回它不知道哪個vc是 – aryaxt

+0

然後你想要一個返回'UIViewController?'的函數,你必須使用'as?'或多個'case' s在'switch'語句中(例如'case是MyViewController:')。泛型被用來爲尚未知的參數提供方法實現,而不允許任意的向下轉換。 – par

+0

「我知道我可以使用投射來解決問題,只是讓方法返回UIViewController或Routable,但我寧願以正確的方式做到這一點在目標c中是可能的,我很驚訝Swift不支持這個 – aryaxt

相關問題