Swift 如何知道从JSON之类的东西实例化哪种类型?

Swift 如何知道从JSON之类的东西实例化哪种类型?,swift,generics,Swift,Generics,我的应用程序在启动时加载一个JSON文件。JSON文件包含创建不同类型的各种实例的说明。作为一个基本示例,我有一个名为BasicType的协议,它带有一些扩展,以使一些内置的Swift类型符合它: protocol BasicType { } extension Int: BasicType { } extension Float: BasicType { } extension String: BasicType { } 我还有一个字典,它将这些类型“名称”(如JSON中所示)映射到

我的应用程序在启动时加载一个JSON文件。JSON文件包含创建不同类型的各种实例的说明。作为一个基本示例,我有一个名为
BasicType
的协议,它带有一些扩展,以使一些内置的Swift类型符合它:

protocol BasicType {

}

extension Int: BasicType { }

extension Float: BasicType { }

extension String: BasicType { }
我还有一个字典,它将这些类型“名称”(如JSON中所示)映射到类型本身:

let basicTypes = [
    "integer": Int.self,
    "float": Float.self,
    "string": String.self
] as [String : BasicType.Type]
extension Int: BasicType {
    static let typeIdentifier = "integer"
}

extension Float: BasicType {
    static let typeIdentifier = "float"
}

extension String: BasicType {
    static let typeIdentifier = "string"
}
当我从JSON加载
BasicType
s时,我使用上面的字典查找JSON指定的名称,以了解要实例化的类型(实际上,协议还定义了初始化器,因此这是可能的)。除了
BasicType
之外,我还有一些其他协议,它们的工作方式完全相同,并且每个协议都有自己的字典映射

例如,我的JSON可能包含我要实例化的
BasicType
s数组:

{
    "basic_types": [{
        "type": "integer",
        ...
    }, {
        "type": "integer",
        ...
    }, {
        "type": "string",
        ...
    }, {
        "type": "float",
        ...
    }]
}
省略号表示传递给初始化器的其他属性,例如整数应该是什么值。实际上,这些是具有各种属性的自定义结构和类。在我的Swift代码中,我打开
basic_types
数组,查看每个JSON对象的
type
键,查找适当的类型并初始化它们。因此,我加载的数组将包括两个
Int
s,一个
String
和一个
Float

例如,这类似于UIKit如何从故事板实例化视图。视图的类名存储在情节提要中,在本例中,它可能使用类似于
NSClassFromString
的内容来执行映射。不过,我不想依赖Objective-C运行时


这种方法的问题是,如果我已经有了类型或实例,就很难查找类型的名称,因为我必须低效地迭代字典进行搜索

相反,我认为更好的解决方案可能是静态地将每个类型的名称(或“类型标识符”)作为变量包含在类型本身中,然后从中生成类型映射。因此,我创建了一个新协议:

protocol TypeIdentifiable {
    static var typeIdentifier: String { get }
}
并使
BasicType
(以及所有其他类似协议)继承自它:

protocol BasicType: TypeIdentifiable {

}
这意味着我需要在类型本身中直接提供名称:

let basicTypes = [
    "integer": Int.self,
    "float": Float.self,
    "string": String.self
] as [String : BasicType.Type]
extension Int: BasicType {
    static let typeIdentifier = "integer"
}

extension Float: BasicType {
    static let typeIdentifier = "float"
}

extension String: BasicType {
    static let typeIdentifier = "string"
}
我制作了一个函数(编译),我打算一般采用符合
typeidentification
的协议,包括一个类型数组,并生成一个包含映射的字典:

func typeMap<T : TypeIdentifiable>(_ types: [T.Type]) -> [String : T.Type] {
    return Dictionary(uniqueKeysWithValues: types.map { type in
        (key: type.typeIdentifier, value: type)
    })
}
这样,如果我有一个类型,我可以通过访问它的静态属性轻松获得它的名称,如果我有这个名称,我可以通过生成的字典获得类型

不幸的是,这不起作用:

错误:无法将类型“[BasicType.type]”的值转换为所需的参数类型“[[UcType.type]”

我认为在Swift中使用协议作为泛型类型是被禁止的,这可能使我不可能完成我想做的事情

我的问题是:有没有一种方法可以做我想做的事情?否则,在更一般的层面上,有没有更好的方法来知道从JSON之类的东西实例化什么类型?

这是而且大体上是“设计的”。协议不符合自身,因此它们无法满足一般要求
BasicType
不能是
T
,因为
BasicType
是一个协议。为什么?好吧,假设我已经编写了这段代码(我希望它与您想要编写的代码非常相似):

该调用什么
init
?Swift通过只允许具体类型符合协议来修复这一僵局。由于
基本类型
不符合
基本类型
,因此它也不符合
类型可识别
,并且不能是
T

那我们该怎么办

  • 第一个也是最重要的一个问题:你真的需要通用吗?您实际计划将它用于完全不同的JSON格式中多少次?您是否可以直接编写解析代码(最好使用新的
    Codeable
    )?试图编写复杂的JSON解析器来解析简单的JSON格式时所洒下的眼泪可能会淹没WWDC。我的经验法则是,除非我已经知道至少3种不同的使用方法,否则不要让事情变得通用。(如果以后发生这种情况,我会在有示例时重构为泛型。)在有几个具体示例之前,您对这个问题的了解还不够,而且您的泛型解决方案可能是错误的

  • 好的,让我们假设这确实是一种非常通用的JSON格式,您必须在不知道其中将包含什么的地方进行解析(可能与您提到的NIB格式类似)。第一个也是最明显的答案:使用现有的解决方案。您所关心的是,很难向后查找它们(类型->标识符),但编写一个方法来简化这一过程并不重要。除非你有数千种,可能数百万种类型,时间上的差异是微不足道的;对于小集合,线性搜索甚至可以比字典更快

  • 接下来要做的就是为每个协议编写
    typeMap
    (即显式地为
    BasicType
    编写,而不是使其通用化。在stdlib中有几种情况下他们会这样做。有时你不得不这样做

  • 当然,你可以创建一个保存信息的
    TypeDefinition
    struct(像一个类型擦除器)。实际上,这只会解决问题。如果有一系列协议,最终你需要复制一些代码

但几乎总是当我走上这种道路时,我发现这是一种过度设计,直接编写愚蠢的代码是一种简单且可扩展的解决方案


当然,请仔细查看Swift 4中新的
JSONDecoder
。如果可以的话,您希望朝着这个方向前进。

如果没有一些具体的例子,很难理解您的问题。您能提供一个
makeOne(what: P.self)