Swift supporta la riflessione?


113

Swift supporta la riflessione? es. c'è qualcosa come valueForKeyPath:e setValue:forKeyPath:per gli oggetti Swift?

In realtà ha anche un sistema di tipi dinamico, qualcosa come obj.classin Objective-C?


1
Ho creato una classe di supporto per la riflessione in Swift. Puoi trovarlo su: github.com/evermeer/EVReflection
Edwin Vermeer

2
Hanno rimosso il riflesso in Swift 2.0. Ecco come enumerare attributi e valori Link
mohacs

Risposte:


85

Sembra che ci sia l'inizio di un supporto di riflessione:

class Fruit {
    var name="Apple"
}

reflect(Fruit()).count         // 1
reflect(Fruit())[0].0          // "name"
reflect(Fruit())[0].1.summary  // "Apple"

Da mchambers gist, qui: https://gist.github.com/mchambers/fb9da554898dae3e54f2


5
Bene, non lo considererei un vero riflesso. Per prima cosa, è di sola lettura. Mi sembra che sia solo un trucco per abilitare il debug in Xcode. Il protocollo Mirrorcita effettivamente la parola IDEpiù volte.
Sulthan

7
E funziona solo per le proprietà. Nessuna riflessione sul metodo.
Sulthan

11
L'autore dell'essenziale ha fatto il check-in. Ho scritto questo nel laboratorio Swift del WWDC, ho pensato di condividerlo con il resto. Come tutti hanno capito, gli ingegneri con cui ho parlato hanno confermato che la funzione reflection () esiste per supportare Playground. Ma puoi ancora divertirti con esso :) qui ho hackerato un piccolo serializzatore di modelli usandolo. Incollalo
Marc Chambers

1
Dai un'occhiata alla risposta stackoverflow.com/a/25345461/292145 per scoprire come _stdlib_getTypeNamepuò aiutarti.
Klaas

1
Ecco una classe che rifletterà le classi di base e gli optional (non i tipi) e ha il supporto per NSCoding e l'analisi da e verso un dizionario: github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/…
Edwin Vermeer

44

Se una classe si estende NSObject, allora tutta l'introspezione e il dinamismo di Objective-C funzionano. Ciò comprende:

  • La capacità di chiedere a una classe i suoi metodi e proprietà e di invocare metodi o impostare proprietà.
  • La capacità di scambiare implementazioni di metodi. (aggiungi funzionalità a tutte le istanze).
  • La possibilità di generare e assegnare una nuova sottoclasse al volo. (aggiungi funzionalità a una determinata istanza)

Un difetto di questa funzionalità è il supporto per i tipi di valore facoltativi di Swift. Ad esempio, le proprietà Int possono essere enumerate e modificate ma Int? le proprietà non possono. I tipi opzionali possono essere enumerati parzialmente utilizzando riflettere / MirrorType, ma non ancora modificati.

Se una classe non si estende NSObject, allora funziona solo la nuova riflessione molto limitata (e in corso?) (Vedi Reflect / MirrorType), che aggiunge una capacità limitata di chiedere a un'istanza la sua classe e le sue proprietà, ma nessuna delle funzionalità aggiuntive sopra .

Quando non si estende NSObject o si utilizza la direttiva "@objc", Swift utilizza per impostazione predefinita l'invio basato su statico e vtable. Questo è più veloce, tuttavia, in assenza di una macchina virtuale non consente l'intercettazione del metodo di runtime. Questa intercettazione è una parte fondamentale di Cocoa ed è necessaria per i seguenti tipi di caratteristiche:

  • Gli eleganti osservatori immobiliari di Cocoa. (Gli osservatori di proprietà sono integrati direttamente nella lingua Swift).
  • Applicazione non invasiva di problematiche trasversali come la registrazione, la gestione delle transazioni (ovvero la programmazione orientata agli aspetti).
  • Proxy, inoltro di messaggi, ecc.

Pertanto si consiglia di classificare nelle applicazioni Cocoa / CocoaTouch implementate con Swift:

  • Estendi da NSObject. La nuova finestra di dialogo della classe in Xcode va in questa direzione.
  • Laddove l'overhead di un invio dinamico porta a problemi di prestazioni, è possibile utilizzare l'invio statico, ad esempio in cicli ristretti con chiamate a metodi con corpi molto piccoli.

Sommario:

  • Swift può comportarsi come C ++, con invio statico / vtable veloce e riflessione limitata. Ciò lo rende adatto per applicazioni di livello inferiore o ad alta intensità di prestazioni, ma senza la complessità, la curva di apprendimento o il rischio di errore associati al C ++
  • Sebbene Swift sia un linguaggio compilato, lo stile di messaggistica dell'invocazione del metodo aggiunge l'introspezione e il dinamismo che si trovano nei linguaggi moderni come Ruby e Python, proprio come Objective-C, ma senza la sintassi legacy di Objective-C.

Dati di riferimento: overhead di esecuzione per invocazioni di metodi:

  • statico: <1,1 ns
  • vtable: ~ 1,1 ns
  • dinamico: ~ 4,9 ns

(le prestazioni effettive dipendono dall'hardware, ma i rapporti rimarranno simili).

Inoltre, l'attributo dinamico ci consente di indicare esplicitamente a Swift che un metodo deve utilizzare l'invio dinamico e quindi supporterà l'intercettazione.

public dynamic func foobar() -> AnyObject {
}

2
Anche usando le tecniche Objective-C sembra non funzionare per i tipi Swift opzionali. Suggerirei di notare questa limitazione nella risposta a meno che non mi perda un trucco.
Whitneyland

8

La documentazione parla di un sistema di tipi dinamico, principalmente di

Type e dynamicType

Vedi Metatype Type (in Language Reference)

Esempio:

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

var type = instance.dynamicType

println("Type: \(type)") //Unfortunately this prints only "Type: Metatype"

Ora assumendo TestObjectsi estendeNSObject

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

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

Attualmente non è stata implementata alcuna riflessione.

EDIT: apparentemente mi sbagliavo, vedi la risposta di Stevex. C'è qualche semplice riflessione di sola lettura per le proprietà incorporate, probabilmente per consentire agli IDE di ispezionare il contenuto degli oggetti.


6

Sembra che un'API di riflessione Swift non sia una priorità assoluta per Apple al momento. Ma oltre alla risposta di @stevex c'è un'altra funzione nella libreria standard che aiuta.

A partire dalla beta 6 _stdlib_getTypeNameottiene il nome del tipo alterato di una variabile. Incolla questo in un playground vuoto:

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

L'output è:

TypeName0 = NSString
TypeName1 = _TtC13__lldb_expr_014PureSwiftClass
TypeName2 = _TtSi
TypeName3 = _TtSS

La voce del blog di Ewan Swick aiuta a decifrare queste stringhe:

ad esempio _TtSista per Inttipo interno di Swift .

Mike Ash ha un ottimo post sul blog che copre lo stesso argomento .


@aleclarson Sì, anche questo è abbastanza utile.
Klaas

1
quelle non sono API private? Apple approverà l'app se viene utilizzata?
Eduardo Costa

@EduardoCosta sì, di sicuro. Sono privati. Li uso solo per build di debug.
Klaas

Ecco un collegamento aggiornato all'articolo del blog di Ewan Swick: eswick.com/2014/06/08/Inside-Swift
RenniePet

5

Potresti prendere in considerazione l'utilizzo di toString () invece. È pubblico e funziona esattamente come _stdlib_getTypeName () con la differenza che funziona anche su AnyClass , ad esempio in un Playground entra

class MyClass {}

toString(MyClass.self) // evaluates to "__lldb_expr_49.MyClass"

1

Nessuna reflectparola chiave in Swift 5, ora puoi usare

struct Person {
    var name="name"
    var age = 15
}

var me = Person()
var mirror = Mirror(reflecting: me)

for case let (label?, value) in mirror.children {
    print (label, value)
}

Perché questo non viene votato? Questo è v utile. Lo applicherò per la jsondeserializzazione
javadba
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.