2014-12-15 16 views
7

Swift ha ereditato il concetto di metaclasse di Objective-C: anche le classi stesse sono considerate oggetti. La classe dell'oggetto della classe è Foo.self ed è di tipo Foo.Type. Se eredita da Bar, allora anche Foo.self può essere assegnato a una variabile di tipo Bar.Type. Questo ha almeno due vantaggi:Posso trasmettere un oggetto metaclass a un tipo di protocollo in Swift?

  • consente di sovrascrivere "metodi statici";
  • è facile creare un'istanza di una classe sconosciuta in un modo sicuro per i caratteri e senza utilizzare il reflection.

Sono particolarmente interessato al secondo in questo momento. Giusto per essere sicuri che tutti capiscano quello che sto cercando, ecco un esempio:

class BaseFoo { 
    var description: String { return "BaseFoo" } 
} 

class DerivedFoo: BaseFoo { 
    override var description: String { return "DerivedFoo" } 
} 

let fooTypes: [BaseFoo.Type] = [BaseFoo.self, DerivedFoo.self] // metaclass magic! 
for type in fooTypes { 
    let object: BaseFoo = type() // metaclass magic! 
    println(object) 
} 

Ora, ho un array di AnyClass oggetti (qualsiasi istanza metaclasse può essere assegnato a AnyClass, proprio come qualsiasi oggetto può essere assegnato a AnyObject) e voglio trovare quali implementano un determinato protocollo. Il protocollo dichiarerebbe un inizializzatore e istanzerei la classe esattamente come faccio nell'esempio sopra. Ad esempio:

protocol Foo { 
    init(foo: String) 
} 

class Bar: Foo { 
    required init(foo: String) { println("Bar initialized with \(foo)") } 
} 

class Baz { 
    required init() { println("I'm not a Foo!") } 
} 

let types: [AnyClass] = [Bar.self, Baz.self] 

Fin qui tutto bene. Ora, il problema è determinare se la classe implementa il protocollo. Poiché le istanze metaclass sono polimorfiche, mi aspetto di poterle trasmettere. Tuttavia, mi pare manca qualcosa, perché Swift non mi permette di scrivere questo:

for type in types { 
    if let fooType = type as? Foo.Type { 
     let obj = fooType(foo: "special snowflake string") 
    } 
} 

L'errore del compilatore che ottengo è:

errore: 'Foo' non è identico a 'AnyObject'

C'è un modo per determinare se un'istanza metaclass rappresenta una classe che implementa un protocollo e c'è un modo per eseguire tale istanza in un tipo di protocollo?

Ho provato a dichiarare Foo come un protocollo di classe, ma apparentemente non è sufficiente.


EDIT: Ho appena provato con il tipo Any, e mentre non causa un errore di sintassi, si blocca il compilatore Swift.

+0

Crashed il compilatore? [File un bug] (http://bugreport.apple.com). – rickster

+0

L'ho già fatto. – zneak

+0

Yay! Per quanto riguarda il problema reale, non sono sicuro che le metaclassi siano introspecibili in Swift attuale. – rickster

risposta

4

A partire da Xcode 7 beta 2 e Swift 2 è stato corretto. A questo punto è possibile scrivere:

for type in types { 
    if let fooType = type as? Foo.Type { 
     // in Swift 2 you have to explicitly call the initializer of metatypes 
     let obj = fooType.init(foo: "special snowflake string") 
    } 
} 

O se desideri solo type come tipo Foo.Type è possibile utilizzare for case

for case let type as Foo.Type in types { 
    let obj = type.init(foo: "special snowflake string") 
} 
Problemi correlati