Snelle taal NSClassFromString

Hoe krijg je reflectiein Swift Language?

Hoe kan ik een klas instantiëren

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

Antwoord 1, autoriteit 100%

Je moet @objc(SwiftClassName)boven je swift-klasse plaatsen.
Vind ik leuk:

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

Antwoord 2, autoriteit 97%

Dit is de manier waarop ik afgeleide UIViewController start op klassenaam

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

Meer informatie is hier

In iOS 9

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

Antwoord 3, autoriteit 63%

Minder hacky-oplossing hier: https://stackoverflow.com/a/32265287/308315

Houd er rekening mee dat Swift-klassen nu een naamruimte hebben, dus in plaats van “MyViewController” zou het “AppName.MyViewController” zijn


Verouderd sinds XCode6-beta 6/7

Oplossing ontwikkeld met XCode6-beta 3

Dankzij het antwoord van Edwin Vermeer kon ik iets bouwen om Swift-klassen in een Obj-C-klasse te instantiëren door dit te doen:

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

BEWERKEN

Je kunt het ook in pure obj-c doen:

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

Ik hoop dat dit iemand zal helpen!


Antwoord 4, autoriteit 46%

UPDATE: vanaf bèta 6 zal NSStringFromClass uw bundelnaam plus klassenaam teruggeven, gescheiden door een punt. Het wordt dus zoiets als MyApp.MyClass

Swift klassen hebben een geconstrueerde interne naam die is opgebouwd uit de volgende delen:

  • Het begint met _TtC,
  • gevolgd door een getal dat overeenkomt met de lengte van uw applicatienaam,
  • gevolgd door de naam van uw toepassing,
  • gevolgd door een getal dat de lengte is van je klasnaam,
  • gevolgd door je klasnaam.

Dus je klasnaam zal zoiets zijn als _TtC5MyApp7MyClass

Je kunt deze naam als een string krijgen door het volgende uit te voeren:

var classString = NSStringFromClass(self.dynamicType)

UpdateIn Swift 3 is dit veranderd in:

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

Met die string kun je een instantie van je Swift-klasse maken door het volgende uit te voeren:

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

Antwoord 5, autoriteit 19%

Het is bijna hetzelfde

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

Controleer dit document:

https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/#//apple_ref/c/func/NSClassFromString


Antwoord 6, autoriteit 15%

Ik heb een object dynamisch kunnen instantiëren

var clazz: NSObject.Type = TestObject.self
var instance : NSObject = clazz()
if let testObject = instance as? TestObject {
    println("yes!")
}

Ik heb geen manier gevonden om AnyClasste maken van een String(zonder Obj-C te gebruiken). Ik denk dat ze niet willen dat je dat doet, omdat het in feite het typesysteem doorbreekt.


Antwoord 7, autoriteit 12%

Voor swift2 heb ik een heel eenvoudige extensie gemaakt om dit sneller te doen
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()
    }
}

In mijn geval doe ik dit om de ViewController te laden die ik wil:

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

Antwoord 8, autoriteit 10%

Hiermee krijg je de naam van de klasse die je wilt instantiëren. Vervolgens kun je Edwins-antwoordgebruiken om een ​​nieuw object van je klas te instantiëren.

Vanaf bèta 6 krijgt _stdlib_getTypeNamede verminkte typenaam van een variabele. Plak dit in een lege speeltuin:

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

De uitvoer is:

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

Ewan Swicks blogbericht helpt bij het ontcijferen van deze strings: http://www. eswick.com/2014/06/inside-swift/

bijv. _TtSistaat voor Swift’s interne Int-type.


Antwoord 9, autoriteit 10%

In Swift 2.0 (getest in de 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)
}

Antwoord 10, autoriteit 8%

xcode 7 bèta 5:

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

Antwoord 11, autoriteit 5%

tekenreeks uit klas

let classString = NSStringFromClass(TestViewController.self)

of

let classString = NSStringFromClass(TestViewController.classForCoder())

initieer een UIViewController-klasse uit string:

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

Antwoord 12, autoriteit 5%

Ik gebruik deze categorie voor 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
    }
}

Het gebruik zou zijn:

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

Antwoord 13, autoriteit 3%

Ik denk dat ik gelijk heb als ik zeg dat dat niet kan, althans niet met de huidige bèta (2). Hopelijk is dit iets dat in toekomstige versies zal veranderen.

Je kunt NSClassFromStringgebruiken om een ​​variabele van het type AnyClasste krijgen, maar er lijkt in Swift geen manier te zijn om deze te instantiëren. Je kunt een brug naar doelstelling C gebruiken en het daar doen of — als het in jouw geval werkt — terugvallen om een ​​switch-statement te gebruiken.


Antwoord 14, autoriteit 3%

Blijkbaar is het niet (meer) mogelijk om een ​​object te instantiëren in Swift als de naam van de klasse alleen bekend is tijdens runtime. Een Objective-C wrapper is mogelijk voor subklassen van NSObject.

Je kunt tenminste een object van dezelfde klasse instantiëren als een ander object dat tijdens runtime is gegeven zonder een Objective-C-wrapper (met xCode Versie 6.2 – 6C107a):

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

Antwoord 15, autoriteit 3%

In Swift 2.0 (getest in de bèta2 van Xcode 7) werkt het als volgt:

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

Het type dat van NSClassFromStringkomt, moet dit init-protocol zeker implementeren.

Ik verwacht dat het duidelijk is, classNameis een string die de Obj-C runtime-naam van de klasse bevat die standaard NIET alleen “Foo” is, maar deze discussie is IMHO niet het belangrijkste onderwerp van uw vraag.

Je hebt dit protocol nodig omdat standaard alle Swift-klassen geen init-methode implementeren.


Antwoord 16, autoriteit 3%

Het lijkt erop dat de juiste bezwering zou zijn…

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
}

…waar “p” staat voor “packaged” – een duidelijk probleem.

Maar de kritieke cast van AnyClass naar T veroorzaakt momenteel een compiler-crash, dus in de tussentijd moet men de initialisatie van k in een aparte afsluiting stoppen, wat prima compileert.


Antwoord 17, autoriteit 3%

Ik gebruik verschillende doelen, en in dit geval wordt de swift-klasse niet gevonden. U moet CFBundleName vervangen door CFBundleExecutable. Ik heb ook de waarschuwingen opgelost:

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

Antwoord 18, autoriteit 3%

Is de oplossing niet zo simpel?

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

Antwoord 19, autoriteit 2%

Ook in Swift 2.0 (mogelijk eerder?) U kunt het type rechtstreeks benaderen met de eigenschap dynamicType

d.w.z.

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)

Uitvoer

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

Werkt voor mijn gebruik


Antwoord 20, autoriteit 2%

Probeer dit.

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

Antwoord 21, autoriteit 2%

Ik heb het zo geïmplementeerd,

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

Antwoord 22, autoriteit 2%

Swift 5, gemakkelijk te gebruiken, dankzij @Ondrej Rafaj’s

  • Broncode:

    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
       }
    }
    
  • Bel als volgt:

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

Antwoord 23

Een voorbeeld van een paginasprong die hier wordt getoond, de hoop kan u helpen!

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

Antwoord 24

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

Antwoord 25

Hier is een goed voorbeeld:

class EPRocks { 
    @require init() { } 
}
class EPAwesome : EPRocks { 
    func awesome() -> String { return "Yes"; } 
}
var epawesome = EPAwesome.self(); 
print(epawesome.awesome);

Other episodes