Hızlı sınıf iç gözlem ve jenerik


121

classJenerik kullanarak dinamik olarak örnek tabanlı bir tür oluşturmaya çalışıyorum , ancak sınıf iç gözleminde zorluk yaşıyorum.

İşte sorular:

  • Obj-C'lerin Swift eşdeğeri var self.classmı?
  • AnyClassSonucunu kullanarak bir sınıfı başlatmanın bir yolu var mı NSClassFromString?
  • AnyClassKesinlikle genel bir parametreden bilgi almanın veya başka bir şekilde yazmanın bir yolu var mı T? (C # typeof(T)sözdizimine benzer)

2
stackoverflow.com/a/24069875/292145 , Swift yansıma API'si hakkında bazı ipuçları verir.
Klaas

5
Objective-C adlı self.classolacaktı self.dynamicType.selfSwift I inancıyla
Filip Hermans

1
Örnek yöntemde, bir sınıf yönteminin nasıl self.dynamicType.foo()

Yanıtlar:


109

Birincisi, Swift eşdeğeri [NSString class]is .self( oldukça zayıf olsalar da Metatype belgelerine bakın ).

Aslında NSString.classçalışmıyor bile! Kullanmalısın NSString.self.

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

Benzer şekilde, hızlı bir sınıfla denedim ...

class MyClass {

}

let MyClassRef = MyClass.self

// ERROR :(
let my_obj = MyClassRef()

Hmm… hata diyor ki:

Oyun alanı yürütme başarısız oldu: hata:: 16: 1: hata: metatip değeri ile 'X' sınıf türünde bir nesne oluşturmak için '@ gerekli' başlatıcı gerekiyor

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

Bunun ne anlama geldiğini anlamam biraz zaman aldı ... ortaya çıktı ki sınıfın bir @required init()

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

    required init () {

    }
}

let Y = X.self

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

Bazı dokümanlar buna şu şekilde değiniyor .Type, ancak MyClass.Typeoyun alanında bana bir hata veriyor.


1
Metatype belgelerine bağlantınız için teşekkür ederiz! Türlerin bu yönünü tamamen gözden kaçırdım, doh!
Erik

14
Sen kullanabilirsiniz .Typeveya .Protocolörneğin değişken bildiriminde, içindelet myObject: MyObject.Type = MyObject.self
Sulthan

1
Sulthan: bu yüzden MyObject.Type bir bildirimdir, ancak MyObject.self bir fabrika yöntemidir (çağrılabilir) ve myObject, bir fabrika yöntemine referans içeren bir değişkendir. MyObject () çağrısı, MyObject sınıfının bir örneğini üretecektir. MyObject değişkeninin adının myObjectFactory olması daha iyi bir örnek olur mu?
bootchk

2
@önce requiredsilinmeli
fujianjin6471

49

İşte nasıl kullanılacağı NSClassFromString. Neyle sonuçlanacağının üst sınıfını bilmelisin. İşte kendilerini nasıl tanımlayacaklarını bilen bir üst sınıf-alt sınıf çifti 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"}
}

@objBu sınıfların Objective-C adlarını dikte etmek için özel sözdiziminin kullanıldığına dikkat edin ; bu çok önemlidir, çünkü aksi takdirde her bir sınıfı belirten munged dizgeyi bilmiyoruz.

Artık NSClassFromStringZork sınıfını veya Zilk sınıfını yapmak için kullanabiliriz , çünkü bunu bir NSObject olarak yazabileceğimizi ve daha sonra çökmeyeceğimizi biliyoruz:

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

Ve tersine çevrilebilir; println(NSStringFromClass(anObject.dynamicType))ayrıca çalışır.


Modern versiyon:

    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
Biraz için oy verin @objc(ClassName). @objcÖzniteliği biliyordum ama sınıf adı hakkında da bir ipucu verebileceğini bilmiyordum .
Erik

1
6 yıldır yazıldığı gibi hala az ya da çok çalışan mükemmel çözüm. Oyun alanının istediği birkaç küçük değişiklik: as! NSObject.Typeilk satırda ve aClass.init()ikinci sırada
Kaji

13

Belgeleri doğru okuyorsam, örneklerle ilgilenirseniz ve örneğin, size verilen nesneden aynı Türün yeni bir örneğini döndürmek isterseniz ve Tür bir init () ile oluşturulabilirse şunları yapabilirsiniz:

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

String ile hızlıca test ettim:

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

hangi iyi çalıştı.


1
Evet, dynamicTypeorada beklediğim gibi çalışıyor. Ancak türleri karşılaştıramadım. Asıl büyük kullanım jenerik ilaçlardır, bu yüzden gibi Generic<T>ve içeride bir şeye sahip olabilirim if T is Double {...}. Görünüşe göre bu maalesef mümkün değil.
Erik

1
@SiLo İki nesnenin aynı sınıftan olup olmadığını genel olarak sormanın bir yolunu buldunuz mu?
matt

1
@matt Zarifçe değil, hayır yapmadım. Ancak, bir oluşturmak başardı DefaultableC # 'ın benzer çalışır protokolü defaultgibi türleri için anahtar kelime ve uygun uzantıları Stringve Int. Genel kısıtlamasını ekleyerek, T:Defaultableargümanın geçip geçmediğini kontrol edebilirim is T.default().
Erik

1
@SiLo Akıllı; Bu kodu görmek isterim! Bunun "eşittir" kullanımındaki garip sınırlamaları aştığını anlıyorum. Bu sınırlamalar ve ayrıca sınıf iç gözleminin genel eksikliği hakkında bir hata bildirdim. NSStringFromClass kullanarak dizeleri karşılaştırmayı bıraktım, ancak elbette bu yalnızca NSObject torunları için çalışıyor.
matt

1
@matt Maalesef kulağa gerçekte olduğundan daha akıllıca geliyor çünkü hala yapmanız gerekiyor value is String.default()... vb, bunun value is Stringyerine bunu yapmak zorunda kalacaksınız .
Erik

13

İçinde ani 3

object.dynamicType

kullanımdan kaldırıldı.

Bunun yerine şunu kullanın:

type(of:object)

7

Türleri karşılaştırmanın hızlı bir şekilde uygulanması

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

NOT: Nesnenin genişletebileceği veya genişletmeyebileceği protokollerle de çalıştığına dikkat edin.


1

Sonunda çalışacak bir şey buldum. Biraz tembel ama NSClassFromString () yolu bile benim için işe yaramadı ...

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

ve bingo, "makeFoo" bir Foo örneği içerir.

Bunun dezavantajı, sınıflarınızın FactoryObject'ten alçalması ve Obj-C + başlatma yöntemine sahip olmaları GEREKİR, böylece sınıfınız "mapClass" genel işlevi tarafından sınıf haritasına otomatik olarak eklenir.


1

Swift'in ilk sürümü için güncellenen, kabul edilen yanıta benzer sınıf hiyerarşisi uygulamasını gösteren başka bir örnek.

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

Bu sonucu yazdırır:

base
file
display
base
folder
display

2
Değişken üst sınıfın Türü ise bu teknik doğru çalışmaz. Örneğin, verilen var x: NamedItem.Typebunu atarsanız, x = Folder.Typeo zaman, x()yeni bir döner NamedItem, bir değil Folder. Bu, tekniği birçok uygulama için işe yaramaz hale getirir. Bunu bir hata olarak görüyorum .
phatmann

1
Aslında, bu tekniği kullanarak istediğinizi düşündüğüm şeyi yapabilirsiniz stackoverflow.com/questions/26290469/…
2014
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.