在swift中使用NSClassFromString实例化嵌套类(Instantiating a nested class using NSClassFromString in swift)

编程入门 行业动态 更新时间:2024-10-26 05:29:55
在swift中使用NSClassFromString实例化嵌套类(Instantiating a nested class using NSClassFromString in swift)

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

@objc class A { @objc class B{ } }

我需要使用NSClassFromString实例化AB 。 我能够为简单的类A做到这一点,但是当我将.B字符串附加到NSClassFromString参数时,它只返回nil。

NSClassFromString("\(appName).A") // works... NSClassFromString("\(appName).A.B") //doesn't work.

我想,由于嵌套类在Objective-c中不可用, NSClassFromString只对嵌套类不起作用......在这种情况下是否有另一种方法从字符串初始化嵌套类?

//编辑

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

"myApp.A" <---- STANDARD CLASS (A) "_TtCC15myApp13A6B" <----- NESTED CLASS (A.B)

如您所见,格式完全不同。 什么是_TtCC15 ? 为什么 ”。” 已被删除? 我想将该格式的类传递给NSClassFromString应该可行。

I have a nested class defined like this:

@objc class A { @objc class B{ } }

and I need to instantiate A.B using NSClassFromString. I was able to do it for the simple class A but when I attach to the NSClassFromString parameter the .B string it just returns nil.

NSClassFromString("\(appName).A") // works... NSClassFromString("\(appName).A.B") //doesn't work.

I suppose that since nested class are not available in Objective-c the NSClassFromString just doesn't work for nested classes... in that case is there another way to initialize a nested class from a string?

// EDIT

is curious how the inverse function NSStringFromClassreturns different format when executed for a standard class and a nested class:

"myApp.A" <---- STANDARD CLASS (A) "_TtCC15myApp13A6B" <----- NESTED CLASS (A.B)

As you can see the format is completely different. What is that _TtCC15? Why the "." has been removed? I suppose that passing the class in that format to NSClassFromString should work.

最满意答案

我发现以下工作在游乐场(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()和func foo(num: Int) -> Bool内部有不同的错位名称。)

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

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

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

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

I find that the following works in a playground (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"

The oddball class "name" string is because the ObjC runtime doesn't understand nested classes (or other kinds of types that Swift can define but ObjC can't) -- within Swift itself, such mangled names are how types, functions and such get uniquely defined. (For example, name mangling is also how function overloading works: func foo() and func foo(num: Int) -> Bool have different mangled names internally.)

The bridging machinery can still be made to dynamically resolve a class given its properly mangled Swift name, but Swift name mangling is an implementation detail and subject to change. (At least until Swift 4 locks down the ABI.) So it's safe to pass the result of NSStringFromClass to NSClassFromString within the same program, but not safe to (e.g.) record a mangled name once in testing, ship that mangled name as a string literal or resource in your app, and expect it to work with NSClassFromString later.

Instead, if you want your nested class (or any other Swift-defined class) to have a reliably known name for use in the ObjC runtime, give it an @objc(name) (as described in the docs):

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

(Note this snippet won't run by itself -- the class A.B has to be referenced at least once somewhere in your process in order to be registered with the ObjC runtime. This could be as simple as putting let t = A.B.self somewhere in your app's startup code.)

更多推荐

本文发布于:2023-08-05 23:58:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1440240.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:嵌套   实例   NSClassFromString   swift   nested

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!