Как добиться отражения в Swift Language?
Как я могу создать экземпляр класса
[[NSClassFromString(@"Foo") alloc] init];
ios
objective-c
swift
Селвин
источник
источник
Ответы:
Здесь меньше хакерского решения: https://stackoverflow.com/a/32265287/308315
Обратите внимание, что классы Swift теперь имеют пространство имен, поэтому вместо «MyViewController» это будет «AppName.MyViewController».
Благодаря ответу Эдвина Вермеера я смог создать что-то для создания экземпляров классов Swift в классе Obj-C, выполнив следующие действия:
// 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 }
РЕДАКТИРОВАТЬ
Вы также можете сделать это в чистом 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); }
Надеюсь, это кому-нибудь поможет!
источник
Вы должны поставить
@objc(SwiftClassName)
выше своего быстрого класса.Подобно:
@objc(SubClass) class SubClass: SuperClass {...}
источник
NSClassFromString()
функции требуется имя, указанное в@objc
атрибуции.@objc(SubClass)
работает, а@objc class SubClass
нет?@objc class SubClass
форме подразумевается, что имя совпадает с именем подкласса . Причем в@objc(SubClass) class SubClass
форме указано прямо. Думаю, компилятор по какой-то причине просто не может разобраться сам в первой форме.Так я инициализирую производный UIViewController по имени класса
var className = "YourAppName.TestViewController" let aClass = NSClassFromString(className) as! UIViewController.Type let viewController = aClass()
Более подробная информация здесь
В iOS 9
var className = "YourAppName.TestViewController" let aClass = NSClassFromString(className) as! UIViewController.Type let viewController = aClass.init()
источник
ОБНОВЛЕНИЕ: начиная с бета-версии 6 NSStringFromClass будет возвращать имя вашего пакета плюс имя класса, разделенное точкой. Так что это будет что-то вроде MyApp.MyClass
У классов Swift будет сконструированное внутреннее имя, состоящее из следующих частей:
Итак, ваше имя класса будет примерно таким: _TtC5MyApp7MyClass
Вы можете получить это имя в виде строки, выполнив:
var classString = NSStringFromClass(self.dynamicType)
Обновление В Swift 3 это изменилось на:
var classString = NSStringFromClass(type(of: self))
Используя эту строку, вы можете создать экземпляр своего класса Swift, выполнив:
var anyobjectype : AnyObject.Type = NSClassFromString(classString) var nsobjectype : NSObject.Type = anyobjectype as NSObject.Type var rec: AnyObject = nsobjectype()
источник
Это почти то же самое
func NSClassFromString(_ aClassName: String!) -> AnyClass!
Проверьте этот документ:
https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/#//apple_ref/c/func/NSClassFromString
источник
NSClass
классами, а не с классами Swift.NSClassFromString("String")
возвращаетсяnil
, ноNSClassFromString("NSString")
не возвращается .var myVar:NSClassFromString("myClassName")
String
- это не класс; это структураNSClassFromString
возвращаетсяnil
для всех классов Swift.Мне удалось создать экземпляр объекта динамически
var clazz: NSObject.Type = TestObject.self var instance : NSObject = clazz() if let testObject = instance as? TestObject { println("yes!") }
Я не нашел способа создать
AnyClass
изString
(без использования Obj-C). Я думаю, они не хотят, чтобы вы это делали, потому что это в основном нарушает систему типов.источник
Для swift2 я создал очень простое расширение, чтобы сделать это быстрее 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() } }
В моем случае я делаю это, чтобы загрузить нужный 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) }
источник
Это даст вам имя класса, который вы хотите создать. Затем вы можете использовать ответ Эдвинса для создания экземпляра нового объекта вашего класса.
Начиная с бета 6,
_stdlib_getTypeName
получает искаженное имя типа переменной. Вставьте это в пустую игровую площадку: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))")
Результат:
TypeName0 = NSString TypeName1 = _TtC13__lldb_expr_014PureSwiftClass TypeName2 = _TtSi TypeName3 = _TtSS
Запись в блоге Юана Свика помогает расшифровать эти строки: http://www.eswick.com/2014/06/inside-swift/
например,
_TtSi
это внутреннийInt
тип Swift .источник
В Swift 2.0 (проверено в 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) }
источник
xcode 7 бета 5:
class MyClass { required init() { print("Hi!") } } if let classObject = NSClassFromString("YOURAPPNAME.MyClass") as? MyClass.Type { let object = classObject.init() }
источник
строка из класса
let classString = NSStringFromClass(TestViewController.self)
или же
let classString = NSStringFromClass(TestViewController.classForCoder())
инициализировать класс UIViewController из строки:
let vcClass = NSClassFromString(classString) as! UIViewController.Type let viewController = vcClass.init()
источник
Я использую эту категорию для 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). Надеюсь, это что-то изменится в будущих версиях.
Вы можете использовать
NSClassFromString
для получения переменной типа,AnyClass
но, похоже, в Swift нет возможности создать ее экземпляр. Вы можете использовать мост к Objective C и сделать это там или - если это работает в вашем случае - вернуться к использованию оператора switch .источник
По-видимому, невозможно (больше) создать экземпляр объекта в Swift, если имя класса известно только во время выполнения. Оболочка Objective-C возможна для подклассов NSObject.
По крайней мере, вы можете создать экземпляр объекта того же класса, что и другой объект, указанный во время выполнения, без оболочки Objective-C (с использованием xCode версии 6.2 - 6C107a):
class Test : NSObject {} var test1 = Test() var test2 = test1.dynamicType.alloc()
источник
В Swift 2.0 (протестирован в бета-версии 2 Xcode 7) это работает так:
protocol Init { init() } var type = NSClassFromString(className) as? Init.Type let obj = type!.init()
Конечно, тип
NSClassFromString
должен реализовывать этот протокол инициализации.Я ожидаю, что это ясно,
className
это строка, содержащая имя среды выполнения Obj-C класса, которое по умолчанию НЕ просто "Foo", но это обсуждение, ИМХО, не является основной темой вашего вопроса.Вам нужен этот протокол, потому что по умолчанию все классы Swift не реализуют
init
метод.источник
Похоже, правильное заклинание было бы ...
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 в отдельное закрытие, которое компилируется нормально.
источник
Я использую разные цели, и в этом случае класс swift не найден. Вам следует заменить CFBundleName на CFBundleExecutable. Я также исправил предупреждения:
- (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); }
источник
Разве решение не так просто?
// Given the app/framework/module named 'MyApp' let className = String(reflecting: MyClass.self) // className = "MyApp.MyClass"
источник
Также в 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 = "" }
Работает для моего варианта использования
источник
Попробуй это.
let className: String = String(ControllerName.classForCoder()) print(className)
источник
Я реализовал вот так,
if let ImplementationClass: NSObject.Type = NSClassFromString(className) as? NSObject.Type{ ImplementationClass.init() }
источник
Swift 5 , простой в использовании, благодаря @Ondrej Rafaj's
Исходный код:
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 } }
Звоните так:
guard let ctrl = "ViewCtrl".controller else { return } // ctrl do sth
источник
Здесь показан пример перехода на страницу, надеюсь, он вам поможет!
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) }
источник
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) } }
источник
Вот хороший пример:
class EPRocks { @require init() { } } class EPAwesome : EPRocks { func awesome() -> String { return "Yes"; } } var epawesome = EPAwesome.self(); print(epawesome.awesome);
источник