Swift 字典如何用于存储通用对象 [T : C<T>]?
How can Swift Dictionaries be used to store generic objects [T : C<T>]?
我在使用 Swift 字典存储通用对象时遇到困难。
我正在尝试以下列方式存储对象:
[C: Container<C>], where C: ContentType
这样字典的结构如下:
Key Value
--- -----
A Container<A>
B Container<B>
...
内容类型
我有一个名为 "ContentType" 的 class,它存储关于一种内容的元数据(即名称、大小、重量等)。
class ContentType: NSObject { ... }
我有许多 "ContentType" 的子class,每个子class 描述了一种特定类型的内容。
class ContentTypeA: ContentType { ... }
class ContentTypeB: ContentType { ... }
容器
我还有一个叫做"Container"的class,它被设计用来存储与内容类型相关的信息。适当地,它是一个泛型 class,其泛型类型为 "ContentType".
class Container<C: ContentType>: NSObject { }
我还在 "Container" 上实现了扩展,为特定 "ContentType" 定义了函数。
extension Container where C: ContentTypeA {
func fancyFunctionOnlyFoundInContentTypeA() { }
}
extension Container where C: ContentTypeB {
func fancyFunctionOnlyFoundInContentTypeB() { }
}
控制器
在我的主控制器class中,我实现了以下下标重载器。
subscript<C: ContentType>(contentType: C) -> Container<C> {
// If Container for ContentType "C" does not exist
if (self.containers[contentType] == nil) {
// Then create it
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
}
// And return it
return self.containers[contentType]! as! Container<C>
}
我们的想法是,您可以 obtain/reuse 一个特定 "ContentType" 的 "Container",它会准备好返回。
// Define a ContentType singletons somewhere
static let ContentTypeASingleton = ContentTypeA.init()
static let ContentTypeBSingleton = ContentTypeA.init()
...
// Using the subscript, the Container for ContentTypeA/ContentTypeB can be obtained
let containerForTypeA = self[ContentTypeASingleton]
let containerForTypeB = self[ContentTypeBSingleton]
从这里,可以调用特定于每个 ContentType 的容器扩展。
containerForTypeA.fancyFunctionOnlyFoundInContentTypeA()
containerForTypeB.fancyFunctionOnlyFoundInContentTypeB()
问题
我相信所有这些在理论上都应该有效,而且它确实可以在 Swift 4、Xcode 9.0 中编译。但是,执行以下命令后:
// Then create it
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
我收到以下错误:
Could not cast value of type 'Test.Container<Test.ContentTypeA>' (0x109824a00) to 'Test.Container<Test.ContentType>' (0x109824638).
由于 ContentTypeA 继承自 ContentType,我不明白为什么这是个问题。似乎 Swift 字典无法存储类型为 ContentType < AnySubclassOfContentType > 的对象,必须将对象存储为 ContentType < ContentType > 。
有人有什么建议吗?干杯。
完整代码如下:
class ViewController: UIViewController {
var containers = [ContentType : Container<ContentType>]()
override func viewDidLoad() {
super.viewDidLoad()
let contentTypeA = ContentTypeA.init()
let contentTypeB = ContentTypeB.init()
let containerForTypeA = self[contentTypeA]
let containerForTypeB = self[contentTypeB]
containerForTypeA.fancyFunctionOnlyFoundInContentTypeA()
containerForTypeB.fancyFunctionOnlyFoundInContentTypeB()
}
subscript<C: ContentType>(contentType: C) -> Container<C> {
if (self.containers[contentType] == nil) {
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
}
return self.containers[contentType]! as! Container<C>
}
}
class ContentType: NSObject { }
class Container<C: ContentType>: NSObject { }
class ContentTypeA: ContentType { }
class ContentTypeB: ContentType { }
extension Container where C: ContentTypeA {
func fancyFunctionOnlyFoundInContentTypeA() { }
}
extension Container where C: ContentTypeB {
func fancyFunctionOnlyFoundInContentTypeB() { }
}
崩溃的发生是因为 Swift 泛型是不变的,因此 Container<B>
不是 Container<A>
的子 class,即使 B
是一个A
的 subclass,任何向下转换都会失败,强制转换会导致崩溃。
您可以通过在字典中使用 AnyObject
作为基础 class 来解决此问题,因为 Container
派生自 NSObject
:
var containers = [ContentType : AnyObject]()
subscript<C: ContentType>(contentType: C) -> Container<C> {
if let container = containers[contentType] {
return container as! Container<C>
} else {
let container = Container<C>()
containers[contentType] = container
return container
}
}
我在使用 Swift 字典存储通用对象时遇到困难。
我正在尝试以下列方式存储对象:
[C: Container<C>], where C: ContentType
这样字典的结构如下:
Key Value
--- -----
A Container<A>
B Container<B>
...
内容类型
我有一个名为 "ContentType" 的 class,它存储关于一种内容的元数据(即名称、大小、重量等)。
class ContentType: NSObject { ... }
我有许多 "ContentType" 的子class,每个子class 描述了一种特定类型的内容。
class ContentTypeA: ContentType { ... }
class ContentTypeB: ContentType { ... }
容器
我还有一个叫做"Container"的class,它被设计用来存储与内容类型相关的信息。适当地,它是一个泛型 class,其泛型类型为 "ContentType".
class Container<C: ContentType>: NSObject { }
我还在 "Container" 上实现了扩展,为特定 "ContentType" 定义了函数。
extension Container where C: ContentTypeA {
func fancyFunctionOnlyFoundInContentTypeA() { }
}
extension Container where C: ContentTypeB {
func fancyFunctionOnlyFoundInContentTypeB() { }
}
控制器
在我的主控制器class中,我实现了以下下标重载器。
subscript<C: ContentType>(contentType: C) -> Container<C> {
// If Container for ContentType "C" does not exist
if (self.containers[contentType] == nil) {
// Then create it
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
}
// And return it
return self.containers[contentType]! as! Container<C>
}
我们的想法是,您可以 obtain/reuse 一个特定 "ContentType" 的 "Container",它会准备好返回。
// Define a ContentType singletons somewhere
static let ContentTypeASingleton = ContentTypeA.init()
static let ContentTypeBSingleton = ContentTypeA.init()
...
// Using the subscript, the Container for ContentTypeA/ContentTypeB can be obtained
let containerForTypeA = self[ContentTypeASingleton]
let containerForTypeB = self[ContentTypeBSingleton]
从这里,可以调用特定于每个 ContentType 的容器扩展。
containerForTypeA.fancyFunctionOnlyFoundInContentTypeA()
containerForTypeB.fancyFunctionOnlyFoundInContentTypeB()
问题
我相信所有这些在理论上都应该有效,而且它确实可以在 Swift 4、Xcode 9.0 中编译。但是,执行以下命令后:
// Then create it
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
我收到以下错误:
Could not cast value of type 'Test.Container<Test.ContentTypeA>' (0x109824a00) to 'Test.Container<Test.ContentType>' (0x109824638).
由于 ContentTypeA 继承自 ContentType,我不明白为什么这是个问题。似乎 Swift 字典无法存储类型为 ContentType < AnySubclassOfContentType > 的对象,必须将对象存储为 ContentType < ContentType > 。
有人有什么建议吗?干杯。
完整代码如下:
class ViewController: UIViewController {
var containers = [ContentType : Container<ContentType>]()
override func viewDidLoad() {
super.viewDidLoad()
let contentTypeA = ContentTypeA.init()
let contentTypeB = ContentTypeB.init()
let containerForTypeA = self[contentTypeA]
let containerForTypeB = self[contentTypeB]
containerForTypeA.fancyFunctionOnlyFoundInContentTypeA()
containerForTypeB.fancyFunctionOnlyFoundInContentTypeB()
}
subscript<C: ContentType>(contentType: C) -> Container<C> {
if (self.containers[contentType] == nil) {
self.containers[contentType] = (Container<C>.init() as! Container<ContentType>)
}
return self.containers[contentType]! as! Container<C>
}
}
class ContentType: NSObject { }
class Container<C: ContentType>: NSObject { }
class ContentTypeA: ContentType { }
class ContentTypeB: ContentType { }
extension Container where C: ContentTypeA {
func fancyFunctionOnlyFoundInContentTypeA() { }
}
extension Container where C: ContentTypeB {
func fancyFunctionOnlyFoundInContentTypeB() { }
}
崩溃的发生是因为 Swift 泛型是不变的,因此 Container<B>
不是 Container<A>
的子 class,即使 B
是一个A
的 subclass,任何向下转换都会失败,强制转换会导致崩溃。
您可以通过在字典中使用 AnyObject
作为基础 class 来解决此问题,因为 Container
派生自 NSObject
:
var containers = [ContentType : AnyObject]()
subscript<C: ContentType>(contentType: C) -> Container<C> {
if let container = containers[contentType] {
return container as! Container<C>
} else {
let container = Container<C>()
containers[contentType] = container
return container
}
}