我发现以下内容在游乐场(Xcode 8.2 / Swift 3)中有效:
// inheriting NSObject is required for `@objc`, at which point `@objc` is optional
class A: NSObject {
class B: NSObject {
override var description: String { return "foo" }
}
}
let str = NSStringFromClass(A.B.self)
guard let anyClass = NSClassFromString(str)
else { fatalError("no class") }
// cast to a type that defines `init()` so we can instantiate
guard let nsClass = anyClass as? NSObject.Type
else { fatalError("class isn't NSObject") }
// call `.init()`, not `nsClass()`; constructor syntax is for static types only
let instance = nsClass.init()
print(instance) // -> "foo"
奇怪的类“名称”字符串是因为 ObjC 运行时不理解嵌套类(或 Swift 可以定义但 ObjC 不能定义的其他类型)——在 Swift 本身内,这种损坏的名称是类型、函数等的方式得到唯一定义。 (例如,名称修饰也是函数重载的工作原理:func foo()
and func foo(num: Int) -> Bool
内部有不同的损坏名称。)
桥接机制仍然可以动态解析给定正确修改的 Swift 名称的类,但 Swift 名称修改是一个实现细节,可能会发生变化。 (至少在 Swift 4 锁定 ABI 之前。)因此传递结果是安全的NSStringFromClass
to NSClassFromString
在同一个程序中,但不安全(例如)在测试中记录一次损坏的名称,将该损坏的名称作为字符串文字或资源发送到您的应用程序中,并期望它能够与NSClassFromString
later.
相反,如果您希望嵌套类(或任何其他 Swift 定义的类)有一个可靠的已知名称以便在 ObjC 运行时使用,请给它一个@objc(name)
(如中所述the docs https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID55):
@objc class A: NSObject {
@objc(A_B) class B: NSObject { /*...*/ }
}
guard let anyClass = NSClassFromString("A_B")
else { fatalError("no class") }
(请注意,此代码片段不会自行运行 - 该类A.B
必须在进程中的某处至少引用一次才能在 ObjC 运行时注册。这可能就像把let t = A.B.self
应用程序启动代码中的某个位置。)