Swift语言NSClassFromString
如何在Swift语言中实现反思 ?
我怎样才能实例化一个类
[[NSClassFromString(@"Foo") alloc] init];
这里更简单的解决scheme: https : //stackoverflow.com/a/32265287/308315
请注意,Swift类现在是命名空间,所以而不是“MyViewController”它将是“AppName.MyViewController”
自XCode6-beta 6/7以来已弃用
使用XCode6-beta 3开发的解决scheme
感谢Edwin Vermeer的回答,我能够通过这样做来将Swift类实例化成一个Obj-C类:
// swift file // extend the NSObject class extension NSObject { // create a static method to get a swift class for a string name class func swiftClassFromString(className: String) -> AnyClass! { // get the project name if var appName: String? = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as String? { // generate the full name of your class (take a look into your "YourProject-swift.h" file) let classStringName = "_TtC\(appName!.utf16count)\(appName)\(countElements(className))\(className)" // return the class! return NSClassFromString(classStringName) } return nil; } } // obj-c file #import "YourProject-Swift.h" - (void)aMethod { Class class = NSClassFromString(key); if (!class) class = [NSObject swiftClassFromString:(key)]; // do something with the class }
编辑
你也可以用纯的obj-c来做:
- (Class)swiftClassFromString:(NSString *)className { NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"]; NSString *classStringName = [NSString stringWithFormat:@"_TtC%d%@%d%@", appName.length, appName, className.length, className]; return NSClassFromString(classStringName); }
我希望这会帮助别人!
你必须把@objc(SwiftClassName)
放在你的swift类的上面。
喜欢:
@objc(SubClass) class SubClass: SuperClass {...}
这是我通过类名初始化UIViewController的方式
var className = "YourAppName.TestViewController" let aClass = NSClassFromString(className) as! UIViewController.Type let viewController = aClass()
更多信息在这里
在iOS 9中
var className = "YourAppName.TestViewController" let aClass = NSClassFromString(className) as! UIViewController.Type let viewController = aClass.init()
更新:从testing版6开始,NSStringFromClass将返回你的包名和类名,用点分隔。 所以它会像MyApp.MyClass
Swift类将有一个构build的内部名称,由以下部分构成:
- 它将从_TtC开始,
- 后跟一个数字,即你的应用程序名称的长度,
- 其次是你的应用程序名称,
- 随着你的class级名字的长度,
- 其次是你的class级名称。
所以你的类名就像_TtC5MyApp7MyClass
您可以通过执行以下命令将其作为string获取:
var classString = NSStringFromClass(self.dynamicType)
在Swift 3中更新,这已经改变为:
var classString = NSStringFromClass(type(of: self))
使用该string,您可以通过执行以下命令来创buildSwift类的实例:
var anyobjectype : AnyObject.Type = NSClassFromString(classString) var nsobjectype : NSObject.Type = anyobjectype as NSObject.Type var rec: AnyObject = nsobjectype()
这几乎是一样的
func NSClassFromString(_ aClassName: String!) -> AnyClass!
检查这个文档:
我能够dynamic地实例化一个对象
var clazz: NSObject.Type = TestObject.self var instance : NSObject = clazz() if let testObject = instance as? TestObject { println("yes!") }
我还没有find一种方法来从String
创buildAnyClass
(不使用Obj-C)。 我认为他们不希望你这样做,因为它基本上打破了types系统。
对于swift2,我创build了一个非常简单的扩展来更快地完成这个任务 https://github.com/damienromito/NSObject-FromClassName
extension NSObject { class func fromClassName(className : String) -> NSObject { let className = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String + "." + className let aClass = NSClassFromString(className) as! UIViewController.Type return aClass.init() } }
在我的情况下,我这样做加载我想要的ViewController:
override func viewDidLoad() { super.viewDidLoad() let controllers = ["SettingsViewController", "ProfileViewController", "PlayerViewController"] self.presentController(controllers.firstObject as! String) } func presentController(controllerName : String){ let nav = UINavigationController(rootViewController: NSObject.fromClassName(controllerName) as! UIViewController ) nav.navigationBar.translucent = false self.navigationController?.presentViewController(nav, animated: true, completion: nil) }
这会得到你想要实例化的类的名字。 然后你可以使用Edwins的答案来实例化你的类的新对象。
从beta 6开始, _stdlib_getTypeName
获取variables的损坏types名称。 将其粘贴到一个空的操场上:
import Foundation class PureSwiftClass { } var myvar0 = NSString() // Objective-C class var myvar1 = PureSwiftClass() var myvar2 = 42 var myvar3 = "Hans" println( "TypeName0 = \(_stdlib_getTypeName(myvar0))") println( "TypeName1 = \(_stdlib_getTypeName(myvar1))") println( "TypeName2 = \(_stdlib_getTypeName(myvar2))") println( "TypeName3 = \(_stdlib_getTypeName(myvar3))")
输出是:
TypeName0 = NSString TypeName1 = _TtC13__lldb_expr_014PureSwiftClass TypeName2 = _TtSi TypeName3 = _TtSS
伊万·斯威克的博客条目有助于破译这些string: http : //www.eswick.com/2014/06/inside-swift/
例如_TtSi
代表Swift的内部Int
types。
在Swift 2.0中(在Xcode 7.01中testing过)_20150930
let vcName = “HomeTableViewController" let ns = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String // Convert string to class let anyobjecType: AnyObject.Type = NSClassFromString(ns + "." + vcName)! if anyobjecType is UIViewController.Type { // vc is instance let vc = (anyobjecType as! UIViewController.Type).init() print(vc) }
xcode 7testing版5:
class MyClass { required init() { print("Hi!") } } if let classObject = NSClassFromString("YOURAPPNAME.MyClass") as? MyClass.Type { let object = classObject.init() }
我认为我是对的,说你不能,至less不能与目前的testing版(2)。 希望这是未来版本将会改变的东西。
你可以使用NSClassFromString
来获取NSClassFromString
types的variables,但是在Swift中似乎没有办法实例化它。 你可以使用桥接到Objective C并在那里执行,或者 – 如果它在你的情况下工作 – 回退到使用switch语句 。
显然,在类中只有在运行时才知道这个类是不可能的(在Swift中)实例化一个对象。 对于NSObject的子类,Objective-C包装器是可能的。
至less你可以实例化一个与运行时给定的另一个对象相同的对象,而不需要Objective-C包装器(使用xCode版本6.2 – 6C107a):
class Test : NSObject {} var test1 = Test() var test2 = test1.dynamicType.alloc()
在Swift 2.0中(在Xcode 7的beta2中testing),它的工作原理是这样的:
protocol Init { init() } var type = NSClassFromString(className) as? Init.Type let obj = type!.init()
当然,来自NSClassFromString
的types必须实现这个init协议。
我期望很清楚, className
是一个string,其中包含默认不只是“Foo”的类的Obj-C运行时名称,但这个讨论恕我直言,不是你的问题的主要议题。
你需要这个协议,因为默认情况下,所有的Swift类都不执行init
方法。
看起来正确的咒语会是…
func newForName<T:NSObject>(p:String) -> T? { var result:T? = nil if let k:AnyClass = NSClassFromString(p) { result = (k as! T).dynamicType.init() } return result }
…“p”代表“包装” – 一个明显的问题。
但是从AnyClass到T的关键转换当前会导致编译器崩溃,因此同时必须将k的初始化放入一个单独的闭包中,这个编译很好。
我使用不同的目标,在这种情况下,没有findswift类。 您应该用CFBundleExecutablereplaceCFBundleName。 我也修正了警告:
- (Class)swiftClassFromString:(NSString *)className { NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"]; NSString *classStringName = [NSString stringWithFormat:@"_TtC%lu%@%lu%@", (unsigned long)appName.length, appName, (unsigned long)className.length, className]; return NSClassFromString(classStringName); }
来自类的string
let classString = NSStringFromClass(TestViewController.self)
要么
let classString = NSStringFromClass(TestViewController.classForCoder())
从string初始化一个UIViewController类:
let vcClass = NSClassFromString(classString) as! UIViewController.Type let viewController = vcClass.init()
也在Swift 2.0中(可能之前?)您可以直接使用dynamicType
属性访问types
即
class User { required init() { // class must have an explicit required init() } var name: String = "" } let aUser = User() aUser.name = "Tom" print(aUser) let bUser = aUser.dynamicType.init() print(bUser)
产量
aUser: User = { name = "Tom" } bUser: User = { name = "" }
适用于我的用例
尝试这个。
let className: String = String(ControllerName.classForCoder()) print(className)
我已经这样实施了,
if let ImplementationClass: NSObject.Type = NSClassFromString(className) as? NSObject.Type{ ImplementationClass.init() }
我正在使用Swift 3的这个类别:
// // String+AnyClass.swift // Adminer // // Created by Ondrej Rafaj on 14/07/2017. // Copyright © 2017 manGoweb UK Ltd. All rights reserved. // import Foundation extension String { func convertToClass<T>() -> T.Type? { return StringClassConverter<T>.convert(string: self) } } class StringClassConverter<T> { static func convert(string className: String) -> T.Type? { guard let nameSpace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String else { return nil } guard let aClass: T.Type = NSClassFromString("\(nameSpace).\(className)") as? T.Type else { return nil } return aClass } }
用途将是:
func getViewController(fromString: String) -> UIViewController? { guard let viewController: UIViewController.Type = "MyViewController".converToClass() else { return nil } return viewController.init() }
这是一个很好的例子:
class EPRocks { @require init() { } } class EPAwesome : EPRocks { func awesome() -> String { return "Yes"; } } var epawesome = EPAwesome.self(); print(epawesome.awesome);