Return Swift 中的实例类型
Return instancetype in Swift
我正在尝试进行此扩展:
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
}
}
但是我得到编译错误:
error: cannot convert return expression of type 'UIViewController' to
return type 'Self'
可能吗?我也想把它变成 init(storyboardName: String, storyboardId: String)
Self
是在编译时确定的,而不是运行时。在您的代码中,Self
完全等同于 UIViewController
,而不是 "the subclass that happens to be calling this." 这将转到 return UIViewController
并且调用者必须 as
它进入右子class。我认为这就是您要避免的(尽管这是 "normal Cocoa" 的方法,所以 returning UIViewController
可能是最好的解决方案)。
注意:在任何情况下都不应将函数命名为 initialize
。这是 NSObject
的现有 class 函数,最好的情况下会造成混乱,最坏的情况下会导致错误。
但是如果你想避免调用者的 as
,subclassing 通常不是在 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
}
这不是 class 方法。这只是一个功能。这里不需要 class。
let tvc: UITableViewController = instantiateViewController(name: name, storyboardId: storyboardId)
与 类似,您可以定义一个通用的辅助方法,它从调用上下文中推断出 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
.
Swift3 的更新:
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)
}
}
另一种方法是使用协议,它还允许您 return 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
}
}
更简洁的解决方案(至少在视觉上更整洁):
Swift 5.1
class func initialize(storyboardName: String, storyboardId: String) -> Self {
return UIStoryboard(name: storyboardName, bundle: nil)
.instantiateViewController(withIdentifier: storyboardId).view as! Self
}
我正在尝试进行此扩展:
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
}
}
但是我得到编译错误:
error: cannot convert return expression of type 'UIViewController' to return type 'Self'
可能吗?我也想把它变成 init(storyboardName: String, storyboardId: String)
Self
是在编译时确定的,而不是运行时。在您的代码中,Self
完全等同于 UIViewController
,而不是 "the subclass that happens to be calling this." 这将转到 return UIViewController
并且调用者必须 as
它进入右子class。我认为这就是您要避免的(尽管这是 "normal Cocoa" 的方法,所以 returning UIViewController
可能是最好的解决方案)。
注意:在任何情况下都不应将函数命名为 initialize
。这是 NSObject
的现有 class 函数,最好的情况下会造成混乱,最坏的情况下会导致错误。
但是如果你想避免调用者的 as
,subclassing 通常不是在 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
}
这不是 class 方法。这只是一个功能。这里不需要 class。
let tvc: UITableViewController = instantiateViewController(name: name, storyboardId: storyboardId)
与
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
.
Swift3 的更新:
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)
}
}
另一种方法是使用协议,它还允许您 return 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
}
}
更简洁的解决方案(至少在视觉上更整洁):
Swift 5.1
class func initialize(storyboardName: String, storyboardId: String) -> Self {
return UIStoryboard(name: storyboardName, bundle: nil)
.instantiateViewController(withIdentifier: storyboardId).view as! Self
}