Comment réaliser la réflexion en Swift Language?
Comment puis-je instancier une classe
[[NSClassFromString(@"Foo") alloc] init];
ios
objective-c
swift
Selvin
la source
la source
Réponses:
Solution moins piratée ici: https://stackoverflow.com/a/32265287/308315
Notez que les classes Swift sont désormais espacées de noms, donc au lieu de "MyViewController", ce serait "AppName.MyViewController"
Grâce à la réponse d'Edwin Vermeer, j'ai pu créer quelque chose pour instancier des classes Swift dans une classe Obj-C en faisant ceci:
// 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 }
ÉDITER
Vous pouvez également le faire en pur 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); }
J'espère que cela aidera quelqu'un!
la source
Vous devez mettre
@objc(SwiftClassName)
au-dessus de votre classe rapide.Comme:
@objc(SubClass) class SubClass: SuperClass {...}
la source
NSClassFromString()
La fonction a besoin d'un nom spécifié par l'@objc
attribution.@objc(SubClass)
fonctionne, mais@objc class SubClass
pas?@objc class SubClass
forme, le nom est implicite comme étant le même que le nom de la sous-classe. Et sous la@objc(SubClass) class SubClass
forme, il est spécifié directement. Je suppose que le compilateur ne peut tout simplement pas le comprendre par lui-même sous la première forme pour une raison quelconque.C'est ainsi que j'initialise UIViewController par nom de classe
var className = "YourAppName.TestViewController" let aClass = NSClassFromString(className) as! UIViewController.Type let viewController = aClass()
Plus d'informations ici
Dans iOS 9
var className = "YourAppName.TestViewController" let aClass = NSClassFromString(className) as! UIViewController.Type let viewController = aClass.init()
la source
MISE À JOUR: À partir de la version bêta 6, NSStringFromClass renverra le nom de votre bundle plus le nom de la classe séparés par un point. Ce sera donc quelque chose comme MyApp.MyClass
Les classes Swift auront un nom interne construit composé des parties suivantes:
Donc, votre nom de classe sera quelque chose comme _TtC5MyApp7MyClass
Vous pouvez obtenir ce nom sous forme de chaîne en exécutant:
var classString = NSStringFromClass(self.dynamicType)
Mise à jour dans Swift 3, ceci est devenu:
var classString = NSStringFromClass(type(of: self))
En utilisant cette chaîne, vous pouvez créer une instance de votre classe Swift en exécutant:
var anyobjectype : AnyObject.Type = NSClassFromString(classString) var nsobjectype : NSObject.Type = anyobjectype as NSObject.Type var rec: AnyObject = nsobjectype()
la source
C'est presque pareil
func NSClassFromString(_ aClassName: String!) -> AnyClass!
Consultez ce document:
https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/#//apple_ref/c/func/NSClassFromString
la source
NSClass
classes, pas les classes Swift.NSClassFromString("String")
revientnil
, maisNSClassFromString("NSString")
ne le fait pas.var myVar:NSClassFromString("myClassName")
String
n'est pas une classe; c'est une structNSClassFromString
revientnil
de toute façon pour toutes les classes Swift.J'ai pu instancier un objet dynamiquement
var clazz: NSObject.Type = TestObject.self var instance : NSObject = clazz() if let testObject = instance as? TestObject { println("yes!") }
Je n'ai pas trouvé de moyen de créer à
AnyClass
partir d'unString
(sans utiliser Obj-C). Je pense qu'ils ne veulent pas que vous fassiez cela parce que cela brise fondamentalement le système de types.la source
Pour swift2, j'ai créé une extension très simple pour le faire plus rapidement 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() } }
Dans mon cas, je fais ceci pour charger le ViewController que je veux:
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) }
la source
Cela vous donnera le nom de la classe que vous souhaitez instancier. Ensuite, vous pouvez utiliser Edwins answer pour instancier un nouvel objet de votre classe.
À partir de la version bêta 6,
_stdlib_getTypeName
obtient le nom de type mutilé d'une variable. Collez ceci dans un terrain de jeu vide: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))")
La sortie est:
TypeName0 = NSString TypeName1 = _TtC13__lldb_expr_014PureSwiftClass TypeName2 = _TtSi TypeName3 = _TtSS
L'entrée de blog d'Ewan Swick aide à déchiffrer ces chaînes: http://www.eswick.com/2014/06/inside-swift/
par exemple
_TtSi
représente leInt
type interne de Swift .la source
Dans Swift 2.0 (testé dans le 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) }
la source
xcode 7 bêta 5:
class MyClass { required init() { print("Hi!") } } if let classObject = NSClassFromString("YOURAPPNAME.MyClass") as? MyClass.Type { let object = classObject.init() }
la source
chaîne de classe
let classString = NSStringFromClass(TestViewController.self)
ou
let classString = NSStringFromClass(TestViewController.classForCoder())
init une classe UIViewController à partir de la chaîne:
let vcClass = NSClassFromString(classString) as! UIViewController.Type let viewController = vcClass.init()
la source
J'utilise cette catégorie pour 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 } }
L'utilisation serait:
func getViewController(fromString: String) -> UIViewController? { guard let viewController: UIViewController.Type = "MyViewController".converToClass() else { return nil } return viewController.init() }
la source
Je pense que j'ai raison de dire que vous ne pouvez pas, du moins pas avec la version bêta actuelle (2). Espérons que c'est quelque chose qui changera dans les versions futures.
Vous pouvez utiliser
NSClassFromString
pour obtenir une variable de typeAnyClass
mais il ne semble y avoir aucun moyen dans Swift de l'instancier. Vous pouvez utiliser un pont vers l'Objectif C et le faire là-bas ou - si cela fonctionne dans votre cas - utiliser une instruction switch .la source
Apparemment, il n'est plus possible (plus) d'instancier un objet dans Swift lorsque le nom de la classe n'est connu qu'au moment de l'exécution. Un wrapper Objective-C est possible pour les sous-classes de NSObject.
Au moins, vous pouvez instancier un objet de la même classe qu'un autre objet donné lors de l'exécution sans wrapper Objective-C (en utilisant xCode Version 6.2 - 6C107a):
class Test : NSObject {} var test1 = Test() var test2 = test1.dynamicType.alloc()
la source
Dans Swift 2.0 (testé dans la version bêta2 de Xcode 7), cela fonctionne comme ceci:
protocol Init { init() } var type = NSClassFromString(className) as? Init.Type let obj = type!.init()
Bien sûr, le type provenant de
NSClassFromString
doit implémenter ce protocole d'initialisation.Je m'attends à ce que ce soit clair,
className
est une chaîne contenant le nom d'exécution Obj-C de la classe qui n'est par défaut PAS juste "Foo", mais cette discussion n'est pas à mon humble avis le sujet principal de votre question.Vous avez besoin de ce protocole car par défaut, toutes les classes Swift n'implémentent pas de
init
méthode.la source
On dirait que l'incantation correcte serait ...
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 }
... où "p" signifie "emballé" - un problème distinct.
Mais la conversion critique d'AnyClass vers T provoque actuellement un plantage du compilateur, donc en attendant, il faut casser l'initialisation de k dans une fermeture séparée, ce qui se compile correctement.
la source
J'utilise des cibles différentes, et dans ce cas, la classe swift n'est pas trouvée. Vous devez remplacer CFBundleName par CFBundleExecutable. J'ai également corrigé les avertissements:
- (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); }
la source
La solution n'est-elle pas aussi simple que cela?
// Given the app/framework/module named 'MyApp' let className = String(reflecting: MyClass.self) // className = "MyApp.MyClass"
la source
Aussi dans Swift 2.0 (peut-être avant?) Vous pouvez accéder au type directement avec la
dynamicType
propriétéc'est à dire
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)
Production
aUser: User = { name = "Tom" } bUser: User = { name = "" }
Fonctionne pour mon cas d'utilisation
la source
Essaye ça.
let className: String = String(ControllerName.classForCoder()) print(className)
la source
J'ai implémenté comme ça,
if let ImplementationClass: NSObject.Type = NSClassFromString(className) as? NSObject.Type{ ImplementationClass.init() }
la source
Swift 5 , facile à utiliser, grâce à @Ondrej Rafaj's
Code source:
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 } }
Appelez comme ceci:
guard let ctrl = "ViewCtrl".controller else { return } // ctrl do sth
la source
Un exemple de saut de page montré ici, l'espoir peut vous aider!
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) }
la source
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) } }
la source
Voici un bon exemple:
class EPRocks { @require init() { } } class EPAwesome : EPRocks { func awesome() -> String { return "Yes"; } } var epawesome = EPAwesome.self(); print(epawesome.awesome);
la source