Swift语言中的NSClassFromString

89

如何在Swift语言中实现反射(reflection)

如何实例化一个类?

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

1
尝试这样写: 如果让 ImplementationClass: NSObject.Type = NSClassFromString(className) as? NSObject.Type{ ImplementationClass.init() } - Pushpa Raja
25个回答

3

类中的字符串

let classString = NSStringFromClass(TestViewController.self)

或者

let classString = NSStringFromClass(TestViewController.classForCoder())

从字符串初始化一个UIViewController类:

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

3
我正在使用这个类别来学习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()
}

2
这个问题的解决方案难道不是如此简单吗?
// Given the app/framework/module named 'MyApp'
let className = String(reflecting: MyClass.self)

// className = "MyApp.MyClass"

3
需要将字符串转换为类,而不是将类转换为字符串。 - Ryan

2
在Swift 2.0中(在Xcode 7的beta2版本中进行了测试),它的工作方式如下:
protocol Init {
  init()
}

var type = NSClassFromString(className) as? Init.Type
let obj = type!.init()

当然,从NSClassFromString返回的类型必须实现此初始化协议。

我希望你清楚,className是一个包含类的Obj-C运行时名称的字符串,默认情况下不仅仅是"Foo",但我认为这不是你问题的主要话题。

你需要这个协议,因为默认情况下所有的Swift类都没有实现init方法。


如果您有纯Swift类,请查看我的其他问题:http://stackoverflow.com/questions/30111659/ - Stephan

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的初始化分解为单独的闭包,这样可以成功编译。


2

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

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

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

这不会从类名创建实例。 - Stephan
从你的回答中并不清楚,它似乎不能在纯Swift类上工作,是吗? - Stephan

2

我认为目前的beta版本(2)不支持,至少目前是不行的。希望未来版本能够改进。

您可以使用NSClassFromString来获取AnyClass类型的变量,但似乎没有办法在Swift中实例化它。您可以使用Objective-C桥接并在那里进行操作,或者--如果在您的情况下起作用--回退到使用switch语句


即使使用Swift 1.2 / XCode 6.3.1,似乎还不可能,或者你找到了解决方案吗? - Stephan

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

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

适用于我的使用情况

1
我已经实现了这样的代码:
if let ImplementationClass: NSObject.Type = NSClassFromString(className) as? NSObject.Type{
   ImplementationClass.init()
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接