Swift Dictionaries如何用于存储通用对象[T:C <t>]?

时间:2017-10-14 23:07:12

标签: swift dictionary generics subscript

我在使用Swift Dictionaries存储通用对象时遇到了困难。

我试图以下列方式存储对象:

[C: Container<C>], where C: ContentType

这样字典的结构如下:

Key        Value
---        -----
A          Container<A>
B          Container<B>
...

的ContentType

我有一个名为“ContentType”的类,它存储有关内容类型的元数据(即名称,大小,重量等)。

class ContentType: NSObject { ... }

我有许多不同的“ContentType”子类,每个子类描述一种特定类型的内容。

class ContentTypeA: ContentType { ... }
class ContentTypeB: ContentType { ... }

容器

我还有一个名为“Container”的类,用于存储与内容类型相关的信息。恰当地,它是一个泛型类,它的泛型类型为“ContentType”。

class Container<C: ContentType>: NSObject { }

我还在“Container”上实现了扩展,为特定的“ContentType”定义了函数。

extension Container where C: ContentTypeA {
    func fancyFunctionOnlyFoundInContentTypeA() { }
}

extension Container where C: ContentTypeB {
    func fancyFunctionOnlyFoundInContentTypeB() { }
}

控制器

在我的主控制器类中,我实现了以下下标overloader。

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

这个想法是你可以获得/重用一个特定“ContentType”的“容器”,它将被准备好。

// 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的Container扩展。

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 Dictionaries无法存储ContentType&lt;类型的对象。 AnySubclassOfContentType&gt; ,并且必须将对象存储为ContentType&lt; ContentType&gt;

有没有人有任何建议?欢呼声。

以下完整代码:

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() { }
}

1 个答案:

答案 0 :(得分:3)

崩溃发生是因为Swift泛型是不变的,因此Container<B>不是Container<A>的子类,即使BA的子类,也不是任何向下的将失败,强迫演员导致崩溃。

您可以使用AnyObject作为字典中的基类来解决此问题,因为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
    }
}