在swift中使用NSClassFromString实例化嵌套类

时间:2017-01-05 14:48:21

标签: swift

我有一个像这样定义的嵌套类:

var showing = 'F';
temp.addEventListener('click', degreeToF);

function degreeToF() {
 if(showing === 'F'){
   // convert to C
   showing = 'C';
   const f = (manchester.current.temp_c - 32 ) * 5/9;
   temp.innerHTML = f.toFixed(0) + '<span class="degrees"> c </span>';
 } else {
   // convert to 
   showing = 'F';
   const f = manchester.current.temp_c * 1.8 + 32;
   temp.innerHTML = f.toFixed(0) + '<span class="degrees"> f </span>';

 }
}

我需要使用@objc class A { @objc class B{ } } 来实例化A.B。我能够为简单的类NSClassFromString执行此操作,但当我将A参数附加到NSClassFromString字符串时,它只返回nil。

.B

我认为由于嵌套类在Objective-c中不可用,NSClassFromString("\(appName).A") // works... NSClassFromString("\(appName).A.B") //doesn't work. 只是不适用于嵌套类...在这种情况下,有另一种方法从字符串初始化嵌套类?

//编辑

很奇怪逆函数NSClassFromString在为标准类和嵌套类执行时如何返回不同的格式:

NSStringFromClass

正如您所看到的,格式完全不同。什么是"myApp.A" <---- STANDARD CLASS (A) "_TtCC15myApp13A6B" <----- NESTED CLASS (A.B) ?为什么&#34;。&#34;已被删除?我认为将该格式的类传递给_TtCC15应该可以。

1 个答案:

答案 0 :(得分:2)

我发现以下内容适用于游乐场(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"

奇怪的类“name”字符串是因为ObjC运行时不理解嵌套类(或Swift可以定义的其他类型但ObjC不能) - 在Swift本身内,这样的错位名称是如何类型的,函数等得到唯一定义。 (例如,名称修改也是函数重载的工作方式:func foo()func foo(num: Int) -> Bool内部具有不同的损坏名称。)

桥接机制仍然可以动态解析一个类,因为它有适当的错误Swift名称,但Swift名称修改是一个实现细节,可能会发生变化。 (至少在Swift 4锁定ABI之前。)因此,在同一程序中将NSStringFromClass的结果传递给NSClassFromString是安全的,但在测试中记录错误的名称是不安全的(例如) ,将名称错误地作为字符串文字或资源添加到您的应用中,并期望它稍后与NSClassFromString一起使用。

相反,如果您希望嵌套类(或任何其他Swift定义的类)具有可靠的已知名称以供ObjC运行时使用,请为其指定@objc(name)(如the docs中所述) ):

@objc class A: NSObject {
    @objc(A_B) class B: NSObject { /*...*/ }
}
guard let anyClass = NSClassFromString("A_B")
    else { fatalError("no class") }

(注意这个片段本身不会运行 - 为了在ObjC运行时注册,必须至少在进程的某个地方引用类A.B。这可能就像放入一样简单在应用的启动代码中的某个地方let t = A.B.self。)