wake-up-neo.com

Schnelle Sprache NSClassFromString

Wie erreicht man Reflektion in Swift Language?

Wie kann ich eine Klasse instanziieren? 

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

Weniger gehackte Lösung hier: https://stackoverflow.com/a/32265287/308315

Beachten Sie, dass Swift-Klassen jetzt einen Namespace haben, so dass anstelle von "MyViewController" "AppName.MyViewController" wäre


Veraltet seit XCode6-beta 6/7

Mit XCode6-beta 3 entwickelte Lösung

Dank der Antwort von Edwin Vermeer konnte ich etwas bauen, um Swift-Klassen in eine Obj-C-Klasse zu instantiieren:

// 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
}

EDIT

Sie können es auch in reinem obj-c tun:

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

Ich hoffe das hilft jemandem!

34
Kevin Delord

Auf diese Weise habe ich UIViewController nach Klassennamen abgeleitet

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

Weitere Informationen finden Sie hier

In iOS 9

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

Sie müssen @objc(SwiftClassName) über Ihre Swift-Klasse stellen.
Mögen: 

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

UPDATE: Ab Beta 6 wird NSStringFromClass Ihren Bundle-Namen plus Klassennamen durch einen Punkt getrennt anzeigen. Es wird also so etwas wie MyApp.MyClass sein

Swift-Klassen haben einen internen Namen, der sich aus den folgenden Teilen zusammensetzt:

  • Es beginnt mit _TtC,
  • gefolgt von einer Nummer, die der Länge Ihres Anwendungsnamens entspricht,
  • gefolgt von Ihrem Anwendungsnamen, 
  • gefolgt von einer Zahl, die der Länge Ihres Klassennamens entspricht,
  • gefolgt von Ihrem Klassennamen.

Ihr Klassenname wird also etwa _TtC5MyApp7MyClass sein

Sie können diesen Namen als Zeichenfolge erhalten, indem Sie Folgendes ausführen:

var classString = NSStringFromClass(self.dynamicType)

Update In Swift 3 wurde dies folgendermaßen geändert:

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

Mit dieser Zeichenfolge können Sie eine Instanz Ihrer Swift-Klasse erstellen, indem Sie Folgendes ausführen:

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

Es ist fast das gleiche

func NSClassFromString(_ aClassName: String!) -> AnyClass!

Überprüfen Sie dieses Dokument:

https://developer.Apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/M Miscellaneous/Foundation_Functions/#//Apple_ref/c/func/NSClassFromString

10
gabuh

Ich konnte ein Objekt dynamisch instanziieren

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

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

Ich habe keine Möglichkeit gefunden, AnyClass aus einer String zu erstellen (ohne Obj-C). Ich denke, sie wollen nicht, dass Sie das tun, weil sie das Typsystem im Grunde bricht.

9
Sulthan

Für Swift2 habe ich eine sehr einfache Erweiterung erstellt, um dies schneller zu erledigenhttps://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()
    }
}

In meinem Fall lade ich den gewünschten 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
Damien Romito

Dadurch erhalten Sie den Namen der Klasse, die Sie instanziieren möchten. Dann können Sie Edwins answer verwenden, um ein neues Objekt Ihrer Klasse zu instanziieren.

Ab Beta 6 erhält _stdlib_getTypeName den unzulässigen Typnamen einer Variablen. Fügen Sie dies in einen leeren Spielplatz ein:

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))")

Die Ausgabe ist:

TypeName0 = NSString
TypeName1 = _TtC13__lldb_expr_014PureSwiftClass
TypeName2 = _TtSi
TypeName3 = _TtSS

Ewan Swicks Blogeintrag hilft beim Entschlüsseln dieser Zeichenfolgen: http://www.eswick.com/2014/06/inside-Swift/

z.B. _TtSi steht für den internen Int-Typ von Swift.

6
Klaas

In Swift 2.0 (getestet im 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)
}
6
Go Let

xcode 7 beta 5:

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

string aus der Klasse 

let classString = NSStringFromClass(TestViewController.self)

oder

let classString = NSStringFromClass(TestViewController.classForCoder())

init eine UIViewController-Klasse aus Zeichenfolge:

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

Sieht aus wie die richtige Beschwörung wäre ...

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
}

... wo "p" für "verpackt" steht - ein eindeutiges Problem.

Die kritische Besetzung von AnyClass nach T führt jedoch zu einem Absturz des Compilers. In der Zwischenzeit muss man die Initialisierung von k in einen separaten Closure aufbrechen, der sich gut kompilieren lässt.

2
rory

In Swift 2.0 (getestet in der Beta2 von Xcode 7) funktioniert es folgendermaßen:

protocol Init {
  init()
}

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

Natürlich muss der Typ, der von NSClassFromString kommt, dieses Init-Protokoll implementieren.

Ich denke, es ist klar, dass className ein String ist, der den Obj-C-Laufzeitnamen der Klasse enthält, der standardmäßig NICHT nur "Foo" ist, aber diese Diskussion ist IMHO nicht das Hauptthema Ihrer Frage.

Sie benötigen dieses Protokoll, da standardmäßig alle Swift-Klassen keine init-Methode implementieren.

2
Stephan

Ich verwende diese Kategorie für 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

    }

}

Die Verwendung wäre:

func getViewController(fromString: String) -> UIViewController? {
    guard let viewController: UIViewController.Type = "MyViewController".converToClass() else {
        return nil
    }
    return viewController.init()
}
2
Ondrej

Ich verwende verschiedene Ziele und in diesem Fall wird die Swift-Klasse nicht gefunden. Sie sollten CFBundleName durch CFBundleExecutable ersetzen. Ich habe auch die Warnungen behoben:

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

Ich glaube, ich kann zu Recht sagen, dass Sie dies nicht tun können, zumindest nicht mit der aktuellen Beta (2). Hoffentlich ändert sich dies in zukünftigen Versionen.

Sie können NSClassFromString verwenden, um eine Variable vom Typ AnyClass abzurufen, aber es scheint keine Möglichkeit zu geben, sie in Swift zu instanziieren. Sie können eine bridge für Objective C verwenden und es dort tun oder - wenn es in Ihrem Fall funktioniert - auf eine switch-Anweisung zurückgreifen .

2

Offensichtlich ist es nicht mehr möglich, ein Objekt in Swift zu instanziieren, wenn der Name der Klasse nur zur Laufzeit bekannt ist. Ein Objective-C-Wrapper ist für Unterklassen von NSObject möglich. 

Sie können zumindest ein Objekt derselben Klasse als ein anderes Objekt zur Laufzeit ohne Objective-C-Wrapper instanziieren (mit xCode Version 6.2 - 6C107a):

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

Ich habe so umgesetzt,

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

Ist die Lösung nicht so einfach?

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

// className = "MyApp.MyClass"
1
MarqueIV

Auch in Swift 2.0 (möglicherweise vorher?) Können Sie mit der Eigenschaft dynamicType direkt auf den Typ zugreifen

d.h.

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)

Ausgabe

aUser: User = {
  name = "Tom"
}
bUser: User = {
  name = ""
}

Funktioniert für meinen Anwendungsfall

1
apocolipse

Versuche dies.

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

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)
    }
}
0
SeRG1k

Ein hier gezeigtes Seitensprungbeispiel, das Ihnen helfen kann!

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)
}
0
Steven