Swift 클래스 인트로 스펙 션 및 제네릭


121

class제네릭을 사용하여 인스턴스 기반 유형 을 동적으로 만들려고 하지만 클래스 내부 검사에 어려움이 있습니다.

질문은 다음과 같습니다.

  • Obj-C와 동등한 Swift가 self.class있습니까?
  • AnyClass결과를 사용하여 클래스를 인스턴스화하는 방법이 NSClassFromString있습니까?
  • AnyClass제네릭 매개 변수에서 엄격하게 정보 를 가져 오거나 입력 하는 방법이 T있습니까? (C #의 typeof(T)구문과 유사 )

2
stackoverflow.com/a/24069875/292145 는 Swift 리플렉션 API에 대한 몇 가지 힌트를 제공합니다.
— Klaas

5
오브젝티브 C의의가 self.class될 것 self.dynamicType.self스위프트 I의 믿음
— 필립 HERMANS에게

1
인스턴스 메서드에서 클래스 메서드를 호출하는 방법은 다음과 같습니다.self.dynamicType.foo()

답변:


109

글쎄, 하나는 Swift와 동등한 것 [NSString class]입니다 .self( 매우 얇지 만 Metatype docs 참조 ).

사실, NSString.class작동하지 않습니다! 을 사용해야 NSString.self합니다.

let s = NSString.self
var str = s()
str = "asdf"

마찬가지로 신속한 수업으로 시도했습니다 ...

class MyClass {

}

let MyClassRef = MyClass.self

// ERROR :(
let my_obj = MyClassRef()

흠… 오류 내용 :

플레이 그라운드 실행 실패 : 오류 : : 16 : 1 : 오류 : 메타 타입 값이있는 클래스 유형 'X'의 개체를 생성하려면 '@required'이니셜 라이저가 필요합니다.

 Y().me()
 ^
 <REPL>:3:7: note: selected implicit initializer with type '()'
 class X {
       ^

이것이 의미하는 바를 알아내는 데 시간이 좀 걸렸습니다. 수업이 @required init()

class X {
    func me() {
        println("asdf")
    }

    required init () {

    }
}

let Y = X.self

// prints "asdf"
Y().me()

일부 문서 .Type에서는 이것을라고 부르지 만 MyClass.Type놀이터에서 오류가 발생합니다.


1
Metatype 문서에 대한 링크를 주셔서 감사합니다! 나는 유형의 측면을 완전히 간과했습니다.
— 에릭

14
.Type또는 .Protocol변수 선언에서 사용할 수 있습니다 . 예 :let myObject: MyObject.Type = MyObject.self
— Sulthan

1
Sulthan : MyObject.Type은 선언이지만 MyObject.self는 팩토리 메서드 (호출 가능)이고 myObject는 팩토리 메서드에 대한 참조를 포함하는 변수입니다. myObject () 호출은 MyObject 클래스의 인스턴스를 생성합니다. myObject 변수의 이름이 myObjectFactory?
— bootchk

2
@전에 required삭제해야
— fujianjin6471

49

를 사용하는 방법은 다음과 같습니다 NSClassFromString. 당신은 당신이 끝내게 될 슈퍼 클래스를 알아야합니다. 다음은 자신을 설명하는 방법을 아는 수퍼 클래스-서브 클래스 쌍입니다 println.

@objc(Zilk) class Zilk : NSObject {
    override var description : String {return "I am a Zilk"}
}

@objc(Zork) class Zork : Zilk {
    override var description : String {return "I am a Zork"}
}

특수 @obj구문을 사용하여 이러한 클래스의 Objective-C 뭉친 이름을 지정합니다. 그렇지 않으면 각 클래스를 지정하는 뭉친 문자열을 알지 못하기 때문에 중요합니다.

이제 NSClassFromStringZork 클래스 또는 Zilk 클래스를 만드는 데 사용할 수 있습니다 . NSObject로 입력하고 나중에 충돌하지 않을 수 있다는 것을 알고 있기 때문입니다.

let aClass = NSClassFromString("Zork") as NSObject.Type
let anObject = aClass()
println(anObject) // "I am a Zork"

그리고 뒤집을 수 있습니다. println(NSStringFromClass(anObject.dynamicType))또한 작동합니다.


최신 버전 :

    if let aClass = NSClassFromString("Zork") as? NSObject.Type {
        let anObject = aClass.init()
        print(anObject) // "I am a Zork"
        print(NSStringFromClass(type(of:anObject))) // Zork
    }

10
@objc(ClassName)비트에 대한 찬성 . @objc속성 에 대해 알고 있었지만 클래스 이름에 대한 힌트를 줄 수 있다는 것은 알지 못했습니다.
— 에릭

1
6 년이 지난 지금도 거의 작동하는 탁월한 솔루션입니다. 놀이터에서 요청한 몇 가지 사소한 조정 : as! NSObject.Type첫 번째 줄과 aClass.init()두 번째 줄
— Kaji

13

문서 권한을 읽고있는 경우 인스턴스를 처리하고 예를 들어 주어진 객체와 동일한 유형의 새 인스턴스를 반환하고 유형을 init ()로 구성 할 수있는 경우 다음을 수행 할 수 있습니다.

let typeOfObject = aGivenObject.dynamicType
var freshInstance = typeOfObject()

String으로 빠르게 테스트했습니다.

let someType = "Fooo".dynamicType
let emptyString = someType()
let threeString = someType("Three")

잘 작동했습니다.


1
네, dynamicType예상대로 작동합니다. 그러나 나는 유형을 비교할 수 없었습니다. 내가 좋아하는 뭔가를 할 수 있도록 실제 큰 사용은 제네릭 함께 Generic<T>내부를 if T is Double {...}. 불행히도 불가능한 것 같습니다.
— Erik

1
@SiLo 일반적으로 두 개체가 같은 클래스인지 물어 보는 방법을 찾았습니까?
— matt

1
@matt 우아하지 않고, 아니에요. 그러나 DefaultableC #의 default키워드 와 유사하게 작동 하는 프로토콜 과 String및 Int. 일반적인 제약 조건을 추가하여 T:Defaultable인수가 전달되었는지 확인할 수 있습니다 is T.default().
— Erik

1
@SiLo Clever; 그 코드를보고 싶습니다! 나는 이것이 "is"의 사용에 대한 이상한 제한을 우회한다고 생각한다. 나는 이러한 한계와 클래스 내성의 일반적인 부족에 대한 버그를 제출했습니다. NSStringFromClass를 사용하여 문자열을 비교했지만 물론 NSObject 하위 항목에서만 작동합니다.
— matt

1
@ 매트는 불행하게도 당신이 아직도해야하기 때문에 실제보다 더 똑똑한 소리 value is String.default(), 등등 ... 그냥 일을 끝낼 것이다 value is String대신.
— Erik

13

에서 빠른 3

object.dynamicType

더 이상 사용되지 않습니다.

대신 다음을 사용하십시오.

type(of:object)

7

유형 비교의 신속한 구현

protocol Decoratable{}
class A:Decoratable{}
class B:Decoratable{}
let object:AnyObject = A()
object.dynamicType is A.Type//true
object.dynamicType is B.Type//false
object.dynamicType is Decoratable.Type//true

참고 : 개체가 확장하거나 확장하지 않을 수있는 프로토콜에서도 작동합니다.


1

마침내 작동 할 무언가를 얻었습니다. 조금 게으르지 만 NSClassFromString () 경로조차도 나를 위해 작동하지 않았습니다 ...

import Foundation

var classMap = Dictionary<String, AnyObject>()

func mapClass(name: String, constructor: AnyObject) -> ()
{
    classMap[name] = constructor;
}

class Factory
{
    class func create(className: String) -> AnyObject?
    {
        var something : AnyObject?

        var template : FactoryObject? = classMap[className] as? FactoryObject

        if (template)
        {
            let somethingElse : FactoryObject = template!.dynamicType()

            return somethingElse
        }

        return nil
    }
}


 import ObjectiveC

 class FactoryObject : NSObject
{
    @required init() {}
//...
}

class Foo : FactoryObject
{
    class override func initialize()
    {
        mapClass("LocalData", LocalData())
    }
    init () { super.init() }
}

var makeFoo : AnyObject? = Factory.create("Foo")

빙고, "makeFoo"는 Foo 인스턴스를 포함합니다.

단점은 클래스가 FactoryObject에서 파생되어야하고 Obj-C + initialize 메서드가 있어야 클래스가 전역 함수 "mapClass"에 의해 클래스 맵에 자동으로 삽입된다는 것입니다.


1

다음은 Swift의 첫 번째 릴리스에 대해 업데이트 된 수락 된 답변과 유사한 클래스 계층 구현을 보여주는 또 다른 예입니다.

class NamedItem : NSObject {
    func display() {
        println("display")
    }

    required override init() {
        super.init()
        println("base")
    }
}

class File : NamedItem {
    required init() {
        super.init()
        println("folder")
    }
}

class Folder : NamedItem {
    required init() {
        super.init()
        println("file")
    }
}

let y = Folder.self
y().display()
let z = File.self
z().display()

이 결과를 인쇄합니다.

base
file
display
base
folder
display

2
이 기술은 변수가 수퍼 클래스의 유형 인 경우 올바르게 작동하지 않습니다. 예를 들어, 주어진 var x: NamedItem.Type내가 그것을 할당하면, x = Folder.Type다음, x()새로운를 반환 NamedItem하는 없습니다 Folder. 이것은 많은 응용 프로그램에서이 기술을 쓸모 없게 만듭니다. 나는 이것을 버그 라고 생각 한다 .
— phatmann 2014

1
실제로이 기술을 사용하여 원하는 작업을 수행 할 수 있습니다. stackoverflow.com/questions/26290469/…
— possen dec
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.