为了账号安全,请及时绑定邮箱和手机立即绑定

在Swift中返回instancetype

在Swift中返回instancetype

慕田峪9158850 2019-08-19 14:12:36
在Swift中返回instancetype我正在尝试进行此扩展:extension UIViewController{     class func initialize(storyboardName: String, storyboardId: String) -> Self     {         let storyboad = UIStoryboard(name: storyboardName, bundle: nil)         let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! Self         return controller    }}但我得到编译错误:错误:无法将'UIViewController'类型的返回表达式转换为返回类型'Self'可能吗?我也想成为init(storyboardName: String, storyboardId: String)
查看完整描述

3 回答

?
米琪卡哇伊

TA贡献1998条经验 获得超6个赞

类似于在Swift中的类扩展函数中使用'self',您可以定义一个通用的辅助方法,它从调用上下文中推断出self的类型:

extension UIViewController{
    class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self
    {
        return instantiateFromStoryboardHelper(storyboardName, storyboardId: storyboardId)
    }

    private class func instantiateFromStoryboardHelper<T>(storyboardName: String, storyboardId: String) -> T    {
        let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
        let controller = storyboard.instantiateViewControllerWithIdentifier(storyboardId) as! T        return controller    }}

然后

let vc = MyViewController.instantiateFromStoryboard("name", storyboardId: "id")

编译,类型推断为MyViewController


Swift 3更新

extension UIViewController{
    class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self
    {
        return instantiateFromStoryboardHelper(storyboardName: storyboardName, storyboardId: storyboardId)
    }

    private class func instantiateFromStoryboardHelper<T>(storyboardName: String, storyboardId: String) -> T    {
        let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
        let controller = storyboard.instantiateViewController(withIdentifier: storyboardId) as! T        return controller    }}

另一种可能的解决方案,使用unsafeDowncast

extension UIViewController{
    class func instantiateFromStoryboard(storyboardName: String, storyboardId: String) -> Self
    {
        let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
        let controller = storyboard.instantiateViewController(withIdentifier: storyboardId)
        return unsafeDowncast(controller, to: self)
    }}


查看完整回答
反对 回复 2019-08-19
?
慕森卡

TA贡献1806条经验 获得超8个赞

Self是在编译时确定的,而不是运行时。在你的代码中,Self完全等同于UIViewController,而不是“恰好调用它的子类”。这将返回UIViewController,调用者必须将as它放入正确的子类中。我认为这是你想要避免的(虽然这是“正常的可可”方式,所以只返回UIViewController可能是最好的解决方案)。

注意:initialize在任何情况下都不应该命名该函数。这是现有的类函数,NSObject并且最多会引起混淆,最坏的情况是错误。

但是如果你想避免调用者的话as,子类化通常不是在Swift中添加功能的工具。相反,您通常需要泛型和协议。在这种情况下,您只需要泛型。

func instantiateViewController<VC: UIViewController>(storyboardName: String, storyboardId: String) -> VC {
    let storyboad = UIStoryboard(name name: storyboardName, bundle: nil)
    let controller = storyboad.instantiateViewControllerWithIdentifier(storyboardId) as! VC    return controller}

这不是一种类方法。这只是一个功能。这里没有必要上课。

let tvc: UITableViewController = instantiateViewController(name: name, storyboardId: storyboardId)


查看完整回答
反对 回复 2019-08-19
?
九州编程

TA贡献1785条经验 获得超4个赞

更清洁的解决方案(至少在视觉上更整洁):

class func initialize(storyboardName: String, storyboardId: String) -> Self {
    // The absurdity that's Swift's type system. If something is possible to do with two functions, why not let it be just one?    func loadFromImpl<T>() -> T {
        let storyboard = UIStoryboard(name: storyboardName, bundle: nil)
        return storyboard.instantiateViewController(withIdentifier: storyboardId).view as! T    }
    return loadFromImpl()}


查看完整回答
反对 回复 2019-08-19
  • 3 回答
  • 0 关注
  • 1477 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信