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:
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 AnyClass
te 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_getTypeName
de 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. _TtSi
staat 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 NSClassFromString
gebruiken om een variabele van het type AnyClass
te 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 NSClassFromString
komt, moet dit init-protocol zeker implementeren.
Ik verwacht dat het duidelijk is, className
is 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);