我正在尝试从一个泛型类型继承,以便我可以将一个类型插入到层次结构中:
class Foo < T:AnyObject > : T {}
但是我遇到了错误
inheritance from non-protocol, non-class type 'T'
但我确保 T 是一个类(即使我将 AnyObject 更改为其他类)。这是否不可能?
我正在尝试从一个泛型类型继承,以便我可以将一个类型插入到层次结构中:
class Foo < T:AnyObject > : T {}
但是我遇到了错误
inheritance from non-protocol, non-class type 'T'
但我确保 T 是一个类(即使我将 AnyObject 更改为其他类)。这是否不可能?
CRTP (Curiously recurring template pattern)和OP中的不同,对吗?
在Swift (<= 3.0)中设计该模式完全没有问题,但目前存在错误,在运行时会在初始化时锁定,而没有任何错误。
class Template<T> { ... }
class RealThing : Template<RealThing> { ... }
最近我在桥接的UIKit API中发现了这个模式。你可以在我的短博客文章这里中阅读。
我还将维基百科上的示例翻译成了Swift 3.0:
protocol Constructor {
init()
}
protocol Shape {
func cloned() -> Shape
}
typealias ShapeProtocols = Constructor & Shape
class Shape_CRTP<T> : ShapeProtocols {
required init() {}
func cloned() -> Shape {
let new = Shape_CRTP<T>()
// copy everything to `new` from `self`
return new
}
}
extension Shape_CRTP where T : ShapeProtocols {
func cloned() -> Shape {
let new = T()
// copy everything to `new` from `self`
return new
}
}
// First non-crtp proof of concept
class Square : ShapeProtocols {
required init() {}
func cloned() -> Shape {
return Square()
}
}
let clone = Shape_CRTP<Square>().cloned() // creates a new `Shape`
type(of: clone) // Square.Type
// now CRTP but this is bugged: http://blog.devandartist.com/posts/swift-crtp
class Circle : Shape_CRTP<Circle> {}
Circle().cloned()
print("this will never print because of the described bug")
如果你不知道某个东西是什么,那么你就不能从它继承,这有多种原因。
首先,编译器需要知道如何在内存中布置你的类的实例。为了做到这一点,它需要知道哪些实例变量(存储属性)它有:包括那些类指定的和从超类继承的。
编译器还需要了解超类的属性和初始化程序,以确保你的类的实例被正确初始化。如果超类的属性是未知的,那么在初始化程序结束之前,就无法知道是否已经初始化了所有应该初始化的状态(而没有初始化任何不应该初始化的状态)。如果超类的初始化程序是未知的,编译器无法强制你从自己的初始化程序中调用超类的指定初始化程序。
如果你不知道超类是什么,那么你将如何编写引用超类属性和方法的方法实现?如果你编写引用self.view
的代码,然后以某种方式实例化一个不继承具有该属性的类的版本,那么该代码将会出错。
可能还有几个原因,但这些应该足够了。 :)
尽管有些地方需要使用子类,但Cocoa(无论是在Swift还是ObjC中)通常更喜欢组合而不是继承进行定制。
如果您要向各种视图控制器添加大量功能,并通过将其分解为自己的类来重用该代码,则简单的解决方案是使您的视图控制器类都拥有该类的实例,而不是尝试成为该类。如果您有想要强制执行某些视图控制器类必须拥有Foo
的位置,则可以定义表达该要求的协议。
如果您要向现有类添加功能,则可以使用扩展。您可能无法使用扩展添加存储属性,但您肯定可以模拟它。例如,您可以使用计算属性,并在其实现中定义自己的存储。其中一种方法可能是使用ObjC运行时的关联对象特性(据我所知,仍可从Swift访问)。
T
在这个库代码中并不为人所知。然而,如果该类在范围上最多为 internal
,那么这种情况可以避免。 - mxcl