如何从 swift 中的字符串生成初始化调用
How can I generate an init call from strings in swift
class A {
let val : Int
init(val: Int) {
self.val = val
}
}
我有这 3 个字符串:
let className = "A"
let argName = "val"
let argValue = "4"
如何使用这 3 个字符串调用 A(val:4)
?
仅在原生 Swift 中,您不能。 Swift 不是动态的,您可以根据 class 的字符串名称实例化任意 class,等等。 Objective-C 是动态的并且有办法做到这一点,所以如果这种事情对你很重要,让 A 成为 NSObject subclass 并将这部分代码写在 Objective-C (或使用等效的 Cocoa/objc-runtime 调用)。
既然您在评论中注意到类型都是某个超类型的子class,那么该超类型可以处理所有调度。在 Cocoa 中,这是一种非常常见的模式,称为 class 簇。
class SuperA {
enum SuperAError: Error {
case cannotConstruct
}
static func create(className: String, argName: String, argValue: String) throws -> SuperA {
switch className {
case "A":
guard argName == "val",
let value = Int(argValue)
else { throw SuperAError.cannotConstruct }
return A(val: value)
default:
throw SuperAError.cannotConstruct
}
}
}
现在,我不是特别喜欢这种方法。这种subclassing往往很差Swift。当您需要引用类型时,Swift 可以与 classes 一起使用,但它不支持 subclassing。我将使用可构建协议和构建器来执行此操作:
enum BuildableError: Error {
case unknownType
case badParameters
}
protocol Buildable {
init(argName: String, argValue: String) throws
// ... and the rest of the methods you require ...
}
struct A {
var val: Int
}
extension A: Buildable {
init(argName: String, argValue: String) throws {
guard argName == "val", let value = Int(argValue) else {
throw BuildableError.badParameters
}
self.init(val: value)
}
}
final class Builder {
var buildables: [String: Buildable.Type] = [:]
func build(className: String, argName: String, argValue: String) throws -> Buildable {
guard let buildable = buildables[className] else {
throw BuildableError.unknownType
}
return try buildable.init(argName: argName, argValue: argValue)
}
}
let builder = Builder()
builder.buildables["A"] = A.self
builder.build(className: "A", argName: "val", argValue: "4")
如果这会导致代码重复,可以使用其他协议直接解决这个问题。例如,如果您的许多类型都有 init(val: Int)
,它们可以与另一个协议共享代码:
protocol ValIntBuildable: Buildable {
init(val: Int)
}
extension ValIntBuildable {
init(argName: String, argValue: String) throws {
guard argName == "val", let value = Int(argValue) else {
throw BuildableError.badParameters
}
self.init(val: value)
}
}
extension A: ValIntBuildable {}
class A {
let val : Int
init(val: Int) {
self.val = val
}
}
我有这 3 个字符串:
let className = "A"
let argName = "val"
let argValue = "4"
如何使用这 3 个字符串调用 A(val:4)
?
仅在原生 Swift 中,您不能。 Swift 不是动态的,您可以根据 class 的字符串名称实例化任意 class,等等。 Objective-C 是动态的并且有办法做到这一点,所以如果这种事情对你很重要,让 A 成为 NSObject subclass 并将这部分代码写在 Objective-C (或使用等效的 Cocoa/objc-runtime 调用)。
既然您在评论中注意到类型都是某个超类型的子class,那么该超类型可以处理所有调度。在 Cocoa 中,这是一种非常常见的模式,称为 class 簇。
class SuperA {
enum SuperAError: Error {
case cannotConstruct
}
static func create(className: String, argName: String, argValue: String) throws -> SuperA {
switch className {
case "A":
guard argName == "val",
let value = Int(argValue)
else { throw SuperAError.cannotConstruct }
return A(val: value)
default:
throw SuperAError.cannotConstruct
}
}
}
现在,我不是特别喜欢这种方法。这种subclassing往往很差Swift。当您需要引用类型时,Swift 可以与 classes 一起使用,但它不支持 subclassing。我将使用可构建协议和构建器来执行此操作:
enum BuildableError: Error {
case unknownType
case badParameters
}
protocol Buildable {
init(argName: String, argValue: String) throws
// ... and the rest of the methods you require ...
}
struct A {
var val: Int
}
extension A: Buildable {
init(argName: String, argValue: String) throws {
guard argName == "val", let value = Int(argValue) else {
throw BuildableError.badParameters
}
self.init(val: value)
}
}
final class Builder {
var buildables: [String: Buildable.Type] = [:]
func build(className: String, argName: String, argValue: String) throws -> Buildable {
guard let buildable = buildables[className] else {
throw BuildableError.unknownType
}
return try buildable.init(argName: argName, argValue: argValue)
}
}
let builder = Builder()
builder.buildables["A"] = A.self
builder.build(className: "A", argName: "val", argValue: "4")
如果这会导致代码重复,可以使用其他协议直接解决这个问题。例如,如果您的许多类型都有 init(val: Int)
,它们可以与另一个协议共享代码:
protocol ValIntBuildable: Buildable {
init(val: Int)
}
extension ValIntBuildable {
init(argName: String, argValue: String) throws {
guard argName == "val", let value = Int(argValue) else {
throw BuildableError.badParameters
}
self.init(val: value)
}
}
extension A: ValIntBuildable {}