Швидкий самоаналіз класу та дженерики


121

Я намагаюся динамічно створити class тип на основі екземпляра, використовуючи generics, однак у мене виникають труднощі з самоаналізами класу.

Ось питання:

  • Чи є Swift-еквівалент Obj-C self.class?
  • Чи є спосіб інстанціювати клас, використовуючи AnyClassрезультат відNSClassFromString ?
  • Чи є спосіб отримати AnyClassабо іншим чином ввести інформацію строго із загального параметра T? (Подібно до typeof(T)синтаксису C # )

2
stackoverflow.com/a/24069875/292145 дає деякі підказки щодо API відображення Swift.
Клаас

5
Objective-C став self.classби self.dynamicType.selfу вірі Свіфт I
Філіп Германс

1
У методі екземпляра, ось як викликати метод класу:self.dynamicType.foo()

Відповіді:


109

Ну, для одного, еквівалент Swift [NSString class]є .self(див. Документи Metatype , хоча вони досить тонкі).

Насправді 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; це важливо, тому що в іншому випадку ми не знаємо гурткового рядка, який позначає кожен клас.

Тепер ми можемо використовувати NSClassFromStringклас Zork або клас 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()в другому
Каджі

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 {...}. Здається, це не можливо на жаль.
Ерік

1
@SiLo Ви коли-небудь знаходили спосіб запитати взагалі, чи два об'єкти одного класу?
мат

1
@matt Не елегантно, ні я цього не робив. Однак мені вдалося створити Defaultableпротокол, який працює аналогічно defaultключовому слову C # , та належні розширення для типів, таких як Stringі Int. Додавши загальне обмеження T:Defaultable, я міг би перевірити, чи перейшов аргумент is T.default().
Ерік

1
@SiLo Розумний; Я хотів би побачити цей код! Я вважаю, що це долає дивні обмеження щодо використання "є". Я подав помилку щодо цих обмежень, а також про загальну відсутність самоаналізу класу. Я закінчив порівнювати рядки за допомогою NSStringFromClass, але, звичайно, це працює лише для нащадків NSObject.
мат

1
@matt На жаль, це здається розумнішим, ніж є насправді, тому що ти все одно маєш робити value is String.default()... і т.д., що ти просто value is Stringнатомість робиш замість цього.
Ерік

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 +, щоб ваш клас автоматично вставлявся в карту класів глобальною функцією "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

1
На насправді ви можете робити те , що я думаю , що ви хочете використовувати цю техніку stackoverflow.com/questions/26290469 / ...
possen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.