所有符合协议的类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 }
为什么添加最后的确定
-
要么你的自定义VC直接从
UIViewController
inheritance,但不需要进一步子类化,所以你应该把它标记为final
。 -
或者,您正在创build一个父级抽象
CommonVC
。 你打算做多个孩子(class CustomVC1: CommonVC
,class 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 { ....