在 Swift 中返回实例类型

问题描述 投票:0回答:4

我正在尝试进行此扩展:

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)

swift swift2 swift-extensions
4个回答
67
投票

在 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) } }
    

15
投票

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)
    

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

斯威夫特 5.1

class func initialize(storyboardName: String, storyboardId: String) -> Self { UIStoryboard(name: storyboardName, bundle: nil) .instantiateViewController(withIdentifier: storyboardId).view as! Self }
    

0
投票
另一种方法是使用协议,它也允许您返回

Self

protocol StoryboardGeneratable { } extension UIViewController: StoryboardGeneratable { } extension StoryboardGeneratable where Self: UIViewController { static func initialize(storyboardName: String, storyboardId: String) -> Self { let storyboad = UIStoryboard(name: storyboardName, bundle: nil) let controller = storyboad.instantiateViewController(withIdentifier: storyboardId) as! Self return controller } }
    
© www.soinside.com 2019 - 2024. All rights reserved.