Swift语言NSClassFromString

时间:2014-06-04 06:48:38

标签: ios objective-c swift

如何在Swift语言中实现reflection

如何实例化一个类

[[NSClassFromString(@"Foo") alloc] init];

25 个答案:

答案 0 :(得分:51)

这是我通过类名

初始化UIViewController的方式
var className = "YourAppName.TestViewController"
let aClass = NSClassFromString(className) as! UIViewController.Type
let viewController = aClass()

更多信息是here

在iOS 9中

var className = "YourAppName.TestViewController"
let aClass = NSClassFromString(className) as! UIViewController.Type
let viewController = aClass.init()

答案 1 :(得分:49)

你必须把@objc(SwiftClassName)放在你的快速课程之上 喜欢:

@objc(SubClass)
class SubClass: SuperClass {...}

答案 2 :(得分:35)

此处不太苛刻的解决方案:https://stackoverflow.com/a/32265287/308315

请注意,Swift类现在是命名空间,因此它不是“MyViewController”而是“AppName.MyViewController”


  

自XCode6-beta 6/7后不推荐使用

     

使用XCode6-beta 3开发的解决方案

感谢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);
}

我希望这会对某人有所帮助!

答案 3 :(得分:26)

更新:从测试版6开始NSStringFromClass将返回您的包名称加上以点分隔的类名。所以它会像MyApp.MyClass

Swift类将具有由以下部分构成的构造内部名称:

  • 它将从_TtC开始,
  • 后跟一个数字,即您的应用程序名称的长度,
  • 后跟您的应用程序名称
  • 下面是一个数字,即你的班级名称的长度,
  • 后跟您的班级名称。

所以你的类名将是_TtC5MyApp7MyClass

您可以通过执行以下命令将此名称作为字符串获取:

var classString = NSStringFromClass(self.dynamicType)

更新在Swift 3中,这已更改为:

var classString = NSStringFromClass(type(of: self))

使用该字符串,您可以通过执行以下命令来创建Swift类的实例:

var anyobjectype : AnyObject.Type = NSClassFromString(classString)
var nsobjectype : NSObject.Type = anyobjectype as NSObject.Type
var rec: AnyObject = nsobjectype()

答案 4 :(得分:10)

答案 5 :(得分:9)

我能够动态实例化一个对象

var clazz: NSObject.Type = TestObject.self
var instance : NSObject = clazz()

if let testObject = instance as? TestObject {
    println("yes!")
}

我还没有找到从AnyClass创建String的方法(不使用Obj-C)。我认为他们不希望你这样做,因为它基本上打破了类型系统。

答案 6 :(得分:7)

对于swift2,我创建了一个非常简单的扩展来更快地执行此操作 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)
}

答案 7 :(得分:6)

这将为您提供要实例化的类的名称。然后,您可以使用Edwins answer来实例化类的新对象。

从beta 6 _stdlib_getTypeName获取变量的受损类型名称。将其粘贴到空的操场上:

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

Ewan Swick的博客文章有助于破译这些字符串:http://www.eswick.com/2014/06/inside-swift/

e.g。 _TtSi代表Swift的内部Int类型。

答案 8 :(得分:6)

在Swift 2.0中(在Xcode 7.01中测试)_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)
}

答案 9 :(得分:5)

xcode 7 beta 5:

class MyClass {
    required init() { print("Hi!") }
}
if let classObject = NSClassFromString("YOURAPPNAME.MyClass") as? MyClass.Type {
    let object = classObject.init()
}

答案 10 :(得分:3)

来自班级

的字符串
let classString = NSStringFromClass(TestViewController.self)

let classString = NSStringFromClass(TestViewController.classForCoder())

从字符串中初始化UIViewController类:

let vcClass = NSClassFromString(classString) as! UIViewController.Type
let viewController = vcClass.init()

答案 11 :(得分:2)

显然,当只在运行时知道类的名称时,不可能(再)在Swift中实例化一个对象。对于NSObject的子类,可以使用Objective-C包装器。

至少你可以在没有Objective-C包装器的情况下实例化与运行时给出的另一个对象相同的类(使用xCode版本6.2 - 6C107a):

    class Test : NSObject {}
    var test1 = Test()
    var test2 = test1.dynamicType.alloc()

答案 12 :(得分:2)

在Swift 2.0中(在Xcode 7的beta2中测试)它的工作原理如下:

NSClassFromString

确保来自className的类型必须实现此init协议。

我希望很清楚,init是一个包含该类的Obj-C运行时名称的字符串,默认情况下不仅仅是" Foo",但这个讨论是恕我直言,不是主要的你问题的主题。

您需要此协议,因为默认情况下,所有Swift类都不会实现{{1}}方法。

答案 13 :(得分:2)

看起来正确的咒语会......

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的初始化破坏成一个单独的闭包,编译得很好。

答案 14 :(得分:2)

我认为我说你不能,至少不是当前的beta(2)。希望这将在未来版本中发生变化。

您可以使用NSClassFromString来获取类型为AnyClass的变量,但似乎没有办法在Swift中实例化它。您可以使用bridge to Objective C and do it there或 - 如果适用于您的情况 - fall back to using a switch statement

答案 15 :(得分:2)

我使用不同的目标,在这种情况下找不到swift类。您应该用CFBundleExecutable替换CFBundleName。我还修正了警告:

- (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);
}

答案 16 :(得分:2)

我在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()
}

答案 17 :(得分:1)

Swift 5 ,易于使用,这要归功于@Ondrej Rafaj的

  • 源代码:

    extension String {
         fileprivate
         func convertToClass<T>() -> T.Type? {
              return StringClassConverter<T>.convert(string: self)
         }
    
    
        var controller: UIViewController?{
              guard let viewController: UIViewController.Type = convertToClass() else {
                return nil
            }
            return viewController.init()
        }
    }
    
    class StringClassConverter<T> {
         fileprivate
         static func convert(string className: String) -> T.Type? {
             guard let nameSpace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String, let aClass = NSClassFromString("\(nameSpace).\(className)") as? T.Type else {
                 return nil
            }
             return aClass
    
       }
    
    }
    
  • 像这样打电话:

    guard let ctrl = "ViewCtrl".controller else {
        return
    }
    //  ctrl do sth
    

答案 18 :(得分:1)

难道解决方案不是这么简单吗?

// Given the app/framework/module named 'MyApp'
let className = String(reflecting: MyClass.self)

// className = "MyApp.MyClass"

答案 19 :(得分:1)

我已经实现了这样,

if let ImplementationClass: NSObject.Type = NSClassFromString(className) as? NSObject.Type{
   ImplementationClass.init()
}

答案 20 :(得分:1)

试试这个。

let className: String = String(ControllerName.classForCoder())
print(className)

答案 21 :(得分:1)

同样在Swift 2.0中(可能在之前?)您可以使用dynamicType属性

直接访问该类型

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 = ""
}

适用于我的用例

答案 22 :(得分:0)

此处显示的页面跳转示例,希望对您有所帮助!

let vc:UIViewController = (NSClassFromString("SwiftAutoCellHeight."+type) as! UIViewController.Type).init()
self.navigationController?.pushViewController(vc, animated: true)

// Click the Table response
tableView.deselectRow(at: indexPath, animated: true)
let sectionModel = models[(indexPath as NSIndexPath).section]
var className = sectionModel.rowsTargetControlerNames[(indexPath as NSIndexPath).row]
className = "GTMRefreshDemo.\(className)"
if let cls = NSClassFromString(className) as? UIViewController.Type {
   let dvc = cls.init()
   self.navigationController?.pushViewController(dvc, animated: true)
}

答案 23 :(得分:0)

<强> Swift3 +

extension String {

    var `class`: AnyClass? {

        guard
            let dict = Bundle.main.infoDictionary,
            var appName = dict["CFBundleName"] as? String
            else { return nil }

        appName.replacingOccurrences(of: " ", with: "_")
        let className = appName + "." + self
        return NSClassFromString(className)
    }
}

答案 24 :(得分:-6)

这是一个很好的例子:

class EPRocks { 
    @require init() { } 
}

class EPAwesome : EPRocks { 
    func awesome() -> String { return "Yes"; } 
}

var epawesome = EPAwesome.self(); 
print(epawesome.awesome);