所有符合协议的类inheritance默认实现

我已经为我的所有UIViewController子类添加了一个方法,它允许我从类和它内部的故事板实例化它。

所有的方法都遵循这种格式:

class func instantiateFromStoryboard() -> CameraViewController? { let storyboard = UIStoryboard(name: "Camera", bundle: nil) let initial = storyboard.instantiateInitialViewController() guard let controller = initial as? CameraViewController else { return nil } return controller } 

相反 ,我想做一个协议, Instantiatable ,需要上面的方法以及一个variablesstoryboardName: String

然后,我想扩展这个Instantiatable所以它包含一个类似于上面的实现。 我的目标是我可以说,一个UIViewController坚持这个协议,我所要定义的是storyboardName

我觉得我接近这个实现:

 protocol Instantiatable { var storyboardName: String { get } func instantiateFromStoryboard() -> Self? } extension Instantiatable where Self: UIViewController { func instantiateFromStoryboard() -> Self? { let storyboard = UIStoryboard(name: storyboardName, bundle: nil) let initial = storyboard.instantiateInitialViewController() guard let controller = initial as? Self else { return nil } return controller } } 

但是,当我尝试添加符合CameraViewController ,我得到的错误:

非终极类CameraViewController方法instantiateFromStoryboard()必须返回Self才能符合协议Instantiatable

我错过了什么?

谢谢。

添加final


这里的解决scheme只是添加final的子类UIViewController (在我的例子中,它是CameraViewController )。

这使您的调用网站能够正确地推断出UIViewController的types而不需要强制转换。 在我的例子中,呼叫站点是:

 guard let controller = CameraViewController.instantiate() else { return } 

但为什么?


为什么添加最后一个关键字很重要?

在与@AliSoftware讨论后,他解释了final的需求。 (他还在Swift mixin仓库中添加了一个类似的协议, 可重用 。)

编译器会关心你的自定义VC是否是final以确保Self需求Instantiatable提及可以被静态推断。

在一个例子中:

 class ParentVC: UIViewController, Instantiatable { // Because of Instantiatable, this class automatically has a method // with this signature: func instantiate() -> ParentVC // here, the "Self" coming from the Protocol, meaning "the conforming class", which is solved as "ParentVC" } class ChildVC: ParentVC { // Error: It inherits from ParentVC, and has to conform // to Instantiatable as a Parent func instantiate() -> ParentVC // but, it also has this from being a Instantiatable object itself func instantiate() -> ChildVC // thus, the compiler cannot solve what "Self" should resolve to here, // either ParentVC or ChildVC. // // Also, it can generate problems in various contexts being "of // both types" here, which is why it's not permitted } 

为什么添加最后的确定


  1. 要么你的自定义VC直接从UIViewControllerinheritance,但不需要进一步子类化,所以你应该把它标记为final

  2. 或者,您正在创build一个父级抽象CommonVC 。 你打算做多个孩子( class CustomVC1: CommonVCclass CustomVC2: CommonVC ),从它inheritance,但在这种情况下, CommonVC是抽象的,可能不会被直接实例化。 因此,不是被标记为Instantiatable ,而是应该将CustomVC1等标记为final + Instantiatable

你可以使用generics来实现你所追求的。 像这样的东西:

 protocol Instantiatable { static func instantiateFromStoryboard<T: UIViewController>() -> T? } extension Instantiatable where Self: UIViewController { static func instantiateFromStoryboard<T: UIViewController>() -> T? { let storyboard = UIStoryboard(name: self.description(), bundle: nil) let initial = storyboard.instantiateInitialViewController() as? T guard let _ = initial as? Self else { return nil } return initial } } 

所以,如果VCA是可instantiatable你可以说let vCA = VCA.InstantiateFromStoryboard()

我把函数改成了一个类函数,这样你就可以在类上调用它,而不需要视图控制器的一个实例。 此代码使用类名来检索故事板文件,但这意味着您的故事板需要被称为projectname.classname.storyboard ,这有点难看。

另一种方法是要求你的视图控制器类实现一个返回故事板名称的函数:

 protocol Instantiatable { // var storyboardName: String { get } static func instantiateFromStoryboard<T: UIViewController>() -> T? static func storyboardName() -> String } extension Instantiatable where Self: UIViewController { static func instantiateFromStoryboard<T: UIViewController>() -> T? { let storyboard = UIStoryboard(name: self.storyboardName(), bundle: nil) let initial = storyboard.instantiateInitialViewController() as? T guard let _ = initial as? Self else { return nil } return initial } } 

然后在每个Instantiatable你需要实现:

 static func storyboardName() -> String { return "ViewController" // (or whatever the storyboard name is) } 

编辑

第三个(也许是最好的)select是让你的UIViewController子类final ,根据你的答案和@ AliSoftware的评论。

这让你使用

 protocol Instantiatable { // var storyboardName: String { get } static func instantiateFromStoryboard() -> Self? static func storyboardName() -> String } extension Instantiatable where Self: UIViewController { static func instantiateFromStoryboard() -> Self? { let storyboard = UIStoryboard(name: self.storyboardName(), bundle: nil) let initial = storyboard.instantiateInitialViewController() as? Self return initial } } 

只要你声明你的视图控制器是:

 final class ViewController: UIViewController, Instantiatable { ....