스위프트 언어 NSClassFromString


83

스위프트 언어로 반영 하는 방법은 무엇입니까?

클래스를 인스턴스화하는 방법

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

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

답변:


37

덜 해키 솔루션 : https://stackoverflow.com/a/32265287/308315

Swift 클래스는 이제 네임 스페이스가 지정되므로 "MyViewController"대신 "AppName.MyViewController"가됩니다.


XCode6-beta 6/7부터 사용되지 않음

XCode6-beta 3을 사용하여 개발 된 솔루션

Edwin Vermeer의 답변 덕분에 다음과 같이 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);
}

나는 이것이 누군가를 도울 수 있기를 바랍니다!


2
베타 7부터는 더 이상 작동하지 않습니다. NSStringFromClass는 이제 번들 이름과 점으로 구분 된 클래스 이름을 반환합니다. 따라서 다음과 같은 코드를 사용할 수 있습니다. var appName : String = NSBundle.mainBundle (). objectForInfoDictionaryKey ( "CFBundleName") as String? let classStringName : String = NSStringFromClass (theObject.dynamicType) return classStringName.stringByReplacingOccurrencesOfString (appName + ".", withString : "", options : NSStringCompareOptions.CaseInsensitiveSearch, range : nil)
Edwin Vermeer 2014 년

@KevinDelord 내 코드에서 귀하의 기술을 사용했습니다. 하지만 앱 이름에 공백이 있으면 appName 변수가 올바른 값을 반환하지 않습니다. 그것을 고치는 방법을 아십니까? 예 : 'App_Name'대신 '앱 이름'
Loadex

countElements 함수를 찾을 수 없습니다. 이것에 대한 도움이 있습니까?
C0D3

대신 이것을 classStringName에 사용합니다. let classStringName = "_TtC (appName! .characters.count) (appName) (className.characters.count) (className)"
C0D3

@Loadex, 실행 파일 이름에 공백이 있으면 작동하지 않습니다. 또한 공백을 밑줄로 바꿔야합니다. 이 (다소 혼란스러운) 블로그 게시물 : medium.com/@maximbilan/…에서 제안되었습니다. 게시물의 코드는 작동했지만 실제 게시물에서는 코드가 수행 한 것과 다른 앱 이름과 대상 이름을 사용하는 것에 대해 이야기했습니다.
stuckj

57

당신은 @objc(SwiftClassName)당신의 신속한 클래스 위에 두어야합니다 .
처럼:

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

2
NSClassFromString()함수에는 @objc속성으로 지정된 이름이 필요 합니다.
eonil 2014 년

1
놀랍고, 내 정신 건강에 큰 영향을 미치는 작은 것!
Adrian_H 2015 년

누군가가 클래스 이름 위에 @objc 일을 한 후에 NSClassFromString ()을 사용하는 방법을 보여줄 수 있습니까? 나는 AnyClass를 얻는다! 반환
라이언 Bobrowski

그것은 나를 위해 작동합니다. 하지만 왜 @objc(SubClass)작동 하는지에 대한 질문이 있지만 @objc class SubClass그렇지 않습니까?
pyanfield

Swift 문서의 @pyanfield : "objc 속성은 선택적으로 식별자로 구성된 단일 속성 인수를 허용합니다. 식별자는 objc 속성이 적용되는 엔티티에 대해 Objective-C에 노출 될 이름을 지정합니다." 따라서 차이점은 Swift 서브 클래스가 Objective C에서 볼 수있는 이름을 얻는 방법입니다. @objc class SubClass형식에서 이름은 SubClass 이름과 동일 함을 암시합니다. 그리고 @objc(SubClass) class SubClass형식으로 직접 지정됩니다. 나는 컴파일러가 어떤 이유로 첫 번째 형태로 스스로 알아낼 수 없다고 생각합니다.
voiger

56

이것은 클래스 이름으로 파생 된 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()

11
응용 프로그램 이름이 포함 된 경우 "-", 그것은 "_"로 대체되어야한다
Zitao 시옹

@RigelChen 좋은 솔루션 감사하지만 유형 (TestViewController)이 필요하고 매번 초기화하고 싶지 않다면 어떻게해야합니까?
ArgaPK

@RyanHeitner 좋은 솔루션 감사하지만 유형 (TestViewController)이 필요하고 매번 초기화하고 싶지 않으면 어떻게해야합니까?
ArgaPK

싱글을 만들고 액세스 @argap이 :하자의 ViewController = aClass.sharedInstance ()
mahboudz

27

업데이트 : 베타 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()

이것은 NSObject 클래스에서만 작동하며 Swift 클래스에 관한 것입니다. 기본적으로 그들은 초기화 방법이 없으며 프로토콜에 대한 typecast가 작동하지 않는 것 같습니다.
Stephan

Swift 1.2 / XCode 6.3.1이 구조에서 제 경우 충돌
Stephan

NSCoding, Printable, Hashable, Equatable 및 JSON을 지원하는 리플렉션 클래스를 만들었습니다. github.com/evermeer/EVReflection
Edwin Vermeer

언급 된 문제는 Swift 2.0에서 수정되었습니다 ... init를 호출해야하기 때문에 내 대답을 참조하십시오 .... nsobjectype () 더 이상 정확하지 않습니다.
Stephan

1
let classString = NSStringFromClass (type (of : self))
Abhishek Thapliyal

11

거의 똑같아

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

이 문서를 확인하십시오.

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


5
이 함수는 NSClassSwift 클래스가 아닌 클래스 에서만 작동합니다 . NSClassFromString("String")를 반환 nil하지만 NSClassFromString("NSString")그렇지 않습니다.
의 Cezary 치크

나는 컴퓨터 앞에 있지 않지만 다음과 같이 시도해 볼 수 있습니다. var myVar:NSClassFromString("myClassName")
gabuh 2014-06-04

2
@CezaryWojcik : ps String는 클래스가 아닙니다. 그것은 구조체입니다
newacct 2014-06-05

2
@newacct D' oh, 당신 말이 맞아요. 그러나 어쨌든 모든 Swift 클래스에 대해서도 NSClassFromString반환 nil됩니다.
Cezary Wojcik 2014-06-09

클래스가 @objc로 주석 처리되거나 NSObject에서 상속 된 경우 Objective-C 개체 시스템을 사용하고 NSClassFromString, 메서드 재구성 등에 참여합니다. 그러나 그렇지 않은 경우 클래스는 Swift 코드 내부에 있으며 그렇지 않습니다. 동일한 객체 시스템을 사용하지 마십시오. 완전히 설명되지는 않았지만 Swift의 객체 시스템은 Objective-C보다 덜 늦게 바인딩 된 것 같습니다.
Bill

9

객체를 동적으로 인스턴스화 할 수있었습니다.

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

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

나는 (Obj-C를 사용하지 않고) AnyClass에서 만드는 방법을 찾지 못했습니다 String. 나는 기본적으로 유형 시스템을 깨뜨리기 때문에 당신이 그렇게하는 것을 원하지 않는다고 생각합니다.


1
이 답변은 NSClassFromString이 아니지만 동적 객체 초기화를 수행하는 Swift 중심의 방법을 제공하는 데 가장 적합한 답변이라고 생각합니다. 공유해 주셔서 감사합니다!
Matt Long

이 응답이 상위에 있어야하는 이유를 강조한 @Sulthan과 Matt에게도 감사드립니다!
Ilias Karim

7

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

인스턴스화하려는 클래스의 이름을 얻을 수 있습니다. 그런 다음 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

Ewan Swick의 블로그 항목은 다음 문자열을 해독하는 데 도움이됩니다. http://www.eswick.com/2014/06/inside-swift/

예를 들어 _TtSiSwift의 내부 Int유형을 나타냅니다 .


"그런 다음 Edwins 답변을 사용하여 클래스의 새 개체를 인스턴스화 할 수 있습니다." 나는 심판이라고 생각하지 않습니다. PureSwiftClass에 대한 답변 작업. 예를 들어 기본적으로이 클래스에는 init 메소드가 없습니다.
Stephan

6

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

5

xcode 7 베타 5 :

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

3

클래스의 문자열

let classString = NSStringFromClass(TestViewController.self)

또는

let classString = NSStringFromClass(TestViewController.classForCoder())

문자열에서 UIViewController 클래스를 초기화합니다.

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

3

이 카테고리를 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

적어도 현재 베타 (2)에서는 불가능하다고 말하는 것이 옳다고 생각합니다. 바라건대 이것은 향후 버전에서 변경 될 것입니다.

를 사용 NSClassFromString하여 유형의 변수를 가져올 수 AnyClass있지만 Swift에서 인스턴스화 할 방법이없는 것 같습니다. Objective C에 대한 브리지를 사용하여 거기에서 수행 하거나-만약 그것이 당신의 경우에 작동한다면 -switch 문을 사용하는 것으로 돌아갈 수 있습니다.


Swift 1.2 / XCode 6.3.1을 사용해도 불가능한 것 같거나 해결책을 찾았습니까?
Stephan

2

분명히, 클래스 이름이 런타임에만 알려지면 Swift에서 객체를 인스턴스화하는 것이 (더 이상) 불가능합니다. Objective-C 래퍼는 NSObject의 하위 클래스에 대해 가능합니다.

최소한 Objective-C 래퍼 (xCode 버전 6.2-6C107a 사용)없이 런타임에 제공된 다른 객체와 동일한 클래스의 객체를 인스턴스화 할 수 있습니다.

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

이것은 클래스 이름에서 인스턴스를 생성하지 않습니다.
Stephan

답변에서 명확하지 않으며 순수한 Swift 클래스에서 작동하지 않는 것 같습니다. 맞습니까?
Stephan

2

Swift 2.0 (Xcode 7의 beta2에서 테스트 됨)에서는 다음과 같이 작동합니다.

protocol Init {
  init()
}

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

에서 오는 유형 NSClassFromString은이 초기화 프로토콜을 구현해야합니다.

나는 그것이 분명하다고 생각하고, className기본적으로 "Foo"가 아닌 클래스의 Obj-C 런타임 이름을 포함하는 문자열이지만이 토론은 IMHO가 귀하의 질문의 주요 주제가 아닙니다.

기본적으로 모든 Swift 클래스는 init메소드를 구현하지 않기 때문에이 프로토콜이 필요합니다 .


당신이 pur Swift 클래스를 가지고 있다면 내 다른 질문을 참조하십시오 : stackoverflow.com/questions/30111659
Stephan

2

올바른 주문은 ...

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"는 "packaged"를 의미합니다. 이는 별개의 문제입니다.

그러나 AnyClass에서 T 로의 중요한 캐스트는 현재 컴파일러 충돌을 일으키므로 그 동안 k 초기화를 별도의 클로저로 버스트해야합니다.


2

다른 타겟을 사용하는데이 경우 신속한 클래스를 찾을 수 없습니다. 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

해결책이 이렇게 간단하지 않습니까?

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

// className = "MyApp.MyClass"

3
원래 질문은 클래스의 문자열이 아닌 문자열의 클래스를 원했습니다.
라이언

1

또한 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

이 시도.

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

1

이렇게 구현했습니다.

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

1

@Ondrej Rafaj 덕분에 사용하기 쉬운 Swift 5

  • 소스 코드:

    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
    

0

여기에 표시된 페이지 점프 예제, 희망이 당신을 도울 수 있습니다!

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

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

제한적이고 즉각적인 도움을 제공 할 수있는이 코드 스 니펫에 감사드립니다. 적절한 설명은 크게 장기 가치를 향상 할 보여줌으로써 이 문제에 대한 좋은 해결책이고, 다른 유사한 질문을 미래의 독자들에게 더 유용 할 것입니다. 제발 편집 당신이 만든 가정 등 일부 설명을 추가 할 답변을.
iBug

-6

다음은 좋은 예입니다.

class EPRocks { 
    @require init() { } 
}

class EPAwesome : EPRocks { 
    func awesome() -> String { return "Yes"; } 
}

var epawesome = EPAwesome.self(); 
print(epawesome.awesome);

1
이것은 질문이 요구하는 것을하는 것이 아닙니다.
ThomasW
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.