Swift 如何知道从JSON之类的东西实例化哪种类型?
我的应用程序在启动时加载一个JSON文件。JSON文件包含创建不同类型的各种实例的说明。作为一个基本示例,我有一个名为Swift 如何知道从JSON之类的东西实例化哪种类型?,swift,generics,Swift,Generics,我的应用程序在启动时加载一个JSON文件。JSON文件包含创建不同类型的各种实例的说明。作为一个基本示例,我有一个名为BasicType的协议,它带有一些扩展,以使一些内置的Swift类型符合它: protocol BasicType { } extension Int: BasicType { } extension Float: BasicType { } extension String: BasicType { } 我还有一个字典,它将这些类型“名称”(如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格式中多少次?您是否可以直接编写解析代码(最好使用新的
)?试图编写复杂的JSON解析器来解析简单的JSON格式时所洒下的眼泪可能会淹没WWDC。我的经验法则是,除非我已经知道至少3种不同的使用方法,否则不要让事情变得通用。(如果以后发生这种情况,我会在有示例时重构为泛型。)在有几个具体示例之前,您对这个问题的了解还不够,而且您的泛型解决方案可能是错误的Codeable
- 好的,让我们假设这确实是一种非常通用的JSON格式,您必须在不知道其中将包含什么的地方进行解析(可能与您提到的NIB格式类似)。第一个也是最明显的答案:使用现有的解决方案。您所关心的是,很难向后查找它们(类型->标识符),但编写一个方法来简化这一过程并不重要。除非你有数千种,可能数百万种类型,时间上的差异是微不足道的;对于小集合,线性搜索甚至可以比字典更快
- 接下来要做的就是为每个协议编写
(即显式地为typeMap
编写,而不是使其通用化。在stdlib中有几种情况下他们会这样做。有时你不得不这样做BasicType
- 当然,你可以创建一个保存信息的
struct(像一个类型擦除器)。实际上,这只会解决问题。如果有一系列协议,最终你需要复制一些代码TypeDefinition
当然,请仔细查看Swift 4中新的
JSONDecoder
。如果可以的话,您希望朝着这个方向前进。如果没有一些具体的例子,很难理解您的问题。您能提供一个
makeOne(what: P.self)