Czy Swift wspiera refleksję?

113

Czy Swift wspiera refleksję? np. czy istnieje coś takiego jak valueForKeyPath:i setValue:forKeyPath:dla obiektów Swift?

Właściwie to ma w ogóle dynamiczny system typów, coś w rodzaju obj.classObjective-C?

Khanh Nguyen
źródło
1
Stworzyłem pomocniczą klasę do refleksji w Swift. Możesz go znaleźć pod adresem: github.com/evermeer/EVReflection
Edwin Vermeer
2
Usunęli odbicie w Swift 2.0. Tak wyliczam atrybuty i wartości. Link
mohacs

Odpowiedzi:

85

Wygląda na to, że jest początek wsparcia dla refleksji:

class Fruit {
    var name="Apple"
}

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

Z istoty mchambers, tutaj: https://gist.github.com/mchambers/fb9da554898dae3e54f2

stevex
źródło
5
Cóż, nie uważałbym tego za prawdziwe odbicie. Po pierwsze, jest tylko do odczytu. Wydaje mi się, że to tylko hack, aby włączyć debugowanie w Xcode. Protokół Mirrorfaktycznie cytuje to słowo IDEkilka razy.
Sulthan
7
I działa tylko w przypadku nieruchomości. Brak refleksji nad metodą.
Sulthan
11
Autor podsumowania. Napisałem to w laboratorium Swift w WWDC, pomyślałem, że resztą podzielę się. Jak wszyscy zorientowali się, inżynierowie, z którymi rozmawiałem, potwierdzili, że funkcja Reflect () obsługuje Playground. Ale nadal możesz się przy tym bawić :) tutaj hackowałem mały serializator modelu, używając go. Wklej go na plac zabaw i baw się dobrze: gist.github.com/mchambers/67640d9c3e2bcffbb1e2
Marc Chambers
1
Zajrzyj na odpowiedź stackoverflow.com/a/25345461/292145, aby dowiedzieć się, jak _stdlib_getTypeNamemożesz pomóc.
Klaas
1
Oto klasa, która odzwierciedla klasy bazowe i opcje (nie typy) oraz obsługuje kodowanie NSCoding i parsowanie zi do słownika: github.com/evermeer/EVCloudKit/blob/master/AppMessage/…
Edwin Vermeer
44

Jeśli klasa się rozszerza NSObject, wtedy cała introspekcja i dynamizm Objective-C działają. To zawiera:

  • Możliwość zapytania klasy o jej metody i właściwości oraz wywoływania metod lub ustawiania właściwości.
  • Możliwość wymiany implementacji metod. (dodaj funkcjonalność do wszystkich instancji).
  • Możliwość generowania i przypisywania nowej podklasy w locie. (dodaj funkcjonalność do danej instancji)

Jedną z wad tej funkcji jest obsługa opcjonalnych typów wartości języka Swift. Na przykład właściwości Int mogą być wyliczane i modyfikowane, ale Int? właściwości nie mogą. Typy opcjonalne można wyliczyć częściowo za pomocą refleksu / MirrorType, ale nadal nie są one modyfikowane.

Jeśli klasa się nie rozszerza NSObject, działa tylko nowe, bardzo ograniczone (i w toku?) Odbicie (patrz odbicie / MirrorType), które dodaje ograniczoną możliwość zadawania instancji pytania o jej klasę i właściwości, ale nie ma żadnych dodatkowych funkcji powyżej .

Jeśli nie rozszerzasz NSObject lub używasz dyrektywy „@objc”, Swift domyślnie korzysta z wysyłek statycznych i vtable. Jest to jednak szybsze, jednak w przypadku braku maszyny wirtualnej nie pozwala na przechwycenie metody w czasie wykonywania. To przechwycenie jest podstawową częścią Cocoa i jest wymagane dla następujących typów funkcji:

  • Eleganccy obserwatorzy nieruchomości Cocoa. (Obserwatorzy nieruchomości są przyzwyczajeni do języka Swift).
  • Nieinwazyjne stosowanie zagadnień przekrojowych, takich jak logowanie, zarządzanie transakcjami (tj. Programowanie zorientowane na aspekty).
  • Serwery proxy, przekazywanie wiadomości itp.

Dlatego zaleca się stosowanie klas w aplikacjach Cocoa / CocoaTouch zaimplementowanych w Swift:

  • Rozszerz z NSObject. Nowe okno dialogowe klasy w Xcode kieruje się w tym kierunku.
  • Tam, gdzie narzut dynamicznej wysyłki prowadzi do problemów z wydajnością, można zastosować wysyłanie statyczne - na przykład w ciasnych pętlach z wywołaniami metod z bardzo małymi treściami.

Podsumowanie:

  • Swift może zachowywać się jak C ++, z szybką wysyłką statyczną / vtable i ograniczoną refleksją. Dzięki temu jest odpowiedni dla aplikacji o niższym poziomie lub wymagających dużej wydajności, ale bez złożoności, krzywej uczenia się lub ryzyka błędu związanego z C ++
  • Podczas gdy Swift jest językiem skompilowanym, styl przesyłania wiadomości w wywołaniu metod dodaje introspekcję i dynamizm występujące we współczesnych językach, takich jak Ruby i Python, podobnie jak Objective-C, ale bez starszej składni Objective-C.

Dane referencyjne: obciążenie związane z wykonywaniem wywołań metod:

  • statyczny: <1,1ns
  • vtable: ~ 1.1ns
  • dynamiczny: ~ 4,9ns

(rzeczywista wydajność zależy od sprzętu, ale wskaźniki pozostaną podobne).

Ponadto atrybut dynamiczny pozwala nam wyraźnie poinstruować Swift, że metoda powinna używać dynamicznego wysyłania, a zatem będzie obsługiwać przechwytywanie.

public dynamic func foobar() -> AnyObject {
}
Jasper Blues
źródło
2
Nawet przy użyciu technik Objective-C wydaje się nie działać dla opcjonalnych typów Swift. Sugerowałbym odnotowanie tego ograniczenia w odpowiedzi, chyba że brakuje mi sztuczki.
whitneyland
8

Dokumentacja mówi o dynamicznym systemie typów, głównie o

Type i dynamicType

Zobacz Typ metatypu (w odwołaniu do języka)

Przykład:

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

var type = instance.dynamicType

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

Teraz zakładając, że się TestObjectrozciągaNSObject

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

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

Obecnie nie ma wdrożonej refleksji.

EDYCJA: Najwyraźniej się myliłem, zobacz odpowiedź Stevexa. Istnieje pewne proste odbicie tylko do odczytu dla wbudowanych właściwości, prawdopodobnie w celu umożliwienia środowiskom IDE inspekcji zawartości obiektu.

Sulthan
źródło
6

Wygląda na to, że interfejs API Swift refleksji nie jest obecnie priorytetem dla Apple. Ale oprócz odpowiedzi @stevex jest jeszcze jedna funkcja w bibliotece standardowej, która pomaga.

Od wersji beta 6 _stdlib_getTypeNamepobiera zniekształconą nazwę typu zmiennej. Wklej to do pustego placu zabaw:

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

Wynik to:

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

Wpis na blogu Ewana Swicka pomaga rozszyfrować te ciągi:

np. _TtSioznacza wewnętrzny Inttyp Swift .

Mike Ash ma świetny wpis na blogu na ten sam temat .

Klaas
źródło
@aleclarson Tak, to też jest bardzo przydatne.
Klaas
1
czy to nie są prywatne API? Czy Apple zatwierdzi aplikację, jeśli jest używana?
Eduardo Costa,
@EduardoCosta tak, na pewno. Są prywatne. Używam ich tylko do debugowania kompilacji.
Klaas
Oto zaktualizowany link do artykułu na blogu Ewana Swicka: eswick.com/2014/06/08/Inside-Swift
RenniePet
5

Możesz zamiast tego rozważyć użycie metody toString () . Jest publiczny i działa tak samo jak _stdlib_getTypeName () z tą różnicą, że działa również na AnyClass , np. W Playground enter

class MyClass {}

toString(MyClass.self) // evaluates to "__lldb_expr_49.MyClass"
jedwabisty zapach
źródło
1

Brak reflectsłowa kluczowego w Swift 5, teraz możesz go użyć

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)
}
Jacky
źródło
Dlaczego nie jest to głosowane za? To jest bardzo przydatne. Mam zamiar zastosować go do jsondeserializacji
javadba