it-swarm.com.ru

Свифт языка NSClassFromString

Как добиться отражения в Swift Language?

Как я могу создать экземпляр класса 

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

Менее хакерское решение здесь: https://stackoverflow.com/a/32265287/308315

Обратите внимание, что классы Swift теперь имеют пространство имен, поэтому вместо «MyViewController» это будет «AppName.MyViewController»


Устаревший с XCode6-бета 6/7

Решение разработано с использованием XCode6-бета 3

Благодаря ответу Эдвина Вермеера я смог создать что-то для создания экземпляров классов 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);
}

Надеюсь, это кому-нибудь поможет!

34
Kevin Delord

Это способ, которым я инициализирую производный 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()
50
Rigel Chen

Вы должны поставить @objc(SwiftClassName) над своим классом Swift.
Подобно: 

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

ОБНОВЛЕНИЕ: Начиная с бета-версии 6 NSStringFromClass вернет имя вашего пакета плюс имя класса, разделенные точкой. Так что это будет что-то вроде MyApp.MyClass

Классы Swift будут иметь составное внутреннее имя, которое состоит из следующих частей:

  • Это начнется с _TtC,
  • сопровождаемый числом, которое является длиной названия вашего приложения,
  • затем имя вашего приложения, 
  • следом за номером, который является длиной имени вашего класса,
  • сопровождаемый вашим именем класса.

Таким образом, ваше имя класса будет что-то вроде _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()
25
Edwin Vermeer

Это почти то же самое

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

Проверьте этот документ:

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

10
gabuh

Я был в состоянии создать экземпляр объекта динамически

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

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

Я не нашел способ создать AnyClass из String (без использования Obj-C). Я думаю, что они не хотят, чтобы вы это делали, потому что это в основном нарушает систему типов.

9
Sulthan

Для 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)
}
7
Damien Romito

Это даст вам имя класса, который вы хотите создать. Затем вы можете использовать ответ Edwins , чтобы создать новый объект вашего класса.

Начиная с бета 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.

6
Klaas

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

строка из класса 

let classString = NSStringFromClass(TestViewController.self)

или же

let classString = NSStringFromClass(TestViewController.classForCoder())

инициализировать класс UIViewController из строки:

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

Похоже, правильное заклинание будет ...

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
}

... где «р» означает «упакованный» - отдельная проблема.

Но критическое приведение от AnyClass к T в настоящее время вызывает сбой компилятора, поэтому в то же время нужно перебросить инициализацию k в отдельное замыкание, которое прекрасно компилируется.

2
rory

В 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.

2
Stephan

Я использую эту категорию для 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
Ondrej

Я использую разные цели, и в этом случае класс 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);
}
2
Calin Drule

Я думаю, что я прав, говоря, что вы не можете, по крайней мере, не с текущей бета-версии (2). Надеюсь, это что-то изменится в будущих версиях.

Вы можете использовать NSClassFromString, чтобы получить переменную типа AnyClass, но в Swift, похоже, нет способа ее создать. Вы можете использовать мост к Objective C и делать это там или - если это работает в вашем случае - прибегнуть к использованию оператора switch .

2
Stephen Darlington

По-видимому, невозможно (больше) создавать экземпляр объекта в Swift, когда имя класса известно только во время выполнения. Оболочка Objective-C возможна для подклассов NSObject. 

По крайней мере, вы можете создать экземпляр объекта того же класса, что и другой объект, заданный во время выполнения, без оболочки Objective-C (используя xCode версии 6.2 - 6C107a):

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

Я реализовал так, как это,

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

Разве решение не так просто, как это?

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

// className = "MyApp.MyClass"
1
MarqueIV

Также в 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 = ""
}

Работает для моего варианта использования

1
apocolipse

Попробуй это.

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

Пример перехода на страницу, показанный здесь, надежда может помочь вам!

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