2014-06-05 9 views
106

Czy Swift wspiera refleksję? na przykład czy istnieje coś takiego jak valueForKeyPath: i setValue:forKeyPath: dla obiektów Swift?Czy Swift wspiera refleksję?

W rzeczywistości ma nawet system dynamicznego typu, coś jak obj.class w Objective-C?

+1

Utworzyłem klasę pomocniczą do refleksji w Swift. Można go znaleźć na stronie: https://github.com/evermeer/EVReflection –

+2

Usunęli odbicie w Swift 2.0. Oto jak wyliczam atrybuty i wartości [link] (http://stackoverflow.com/questions/25463146/iterate-over-object-class-attributes-in-swift/32969324#32969324) – mohacs

Odpowiedz

83

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

class Fruit { 
    var name="Apple" 
} 

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

Od mchambers istota, tutaj: https://gist.github.com/mchambers/fb9da554898dae3e54f2

+0

Dzięki. Bardzo pomocne informacje! –

+5

Cóż, nie uważam tego za prawdziwą refleksję. Po pierwsze, jest to tylko do odczytu. Wydaje mi się, że to po prostu hack, aby włączyć debugowanie w Xcode. Protokół 'Odbicie' faktycznie cytuje słowo' IDE' kilka razy. – Sulthan

+7

I działa tylko dla właściwości. Bez refleksji metodologicznej. – Sulthan

8

Dokumentacja mówi o dynamiczny system typów, głównie o

Type i dynamicType

Zobacz Metatype Type (in Language Reference)

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 TestObject rozciąga NSObject

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

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

Obecnie nie wdrożono żadnych refleksji.

EDIT: Byłem najwyraźniej w błędzie, zobacz odpowiedź Stevex. Istnieje pewna prosta refleksja na temat odczytu wbudowanych właściwości, prawdopodobnie w celu umożliwienia kontrolerom IDE sprawdzania zawartości obiektów.

42

Jeśli klasa rozszerza NSObject, to wszystkie introspekcji Objective-C i dynamizm działa. Obejmuje to:

  • Możliwość zadawania pytań na temat klas dotyczących metod 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 konkretnej instancji)

Jednym z niedociągnięć tej funkcji jest obsługa opcjonalnych typów wartości Swift. Na przykład właściwości Int można wyliczyć i zmodyfikować, ale Int? właściwości nie mogą. Typy opcjonalne mogą być wyliczane częściowo za pomocą parametru reflect/MirrorType, ale wciąż nie są modyfikowane.

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

Gdy nie rozszerzasz NSObject lub używasz dyrektywy "@objc", Swift domyślnie stosuje wysyłkę statyczną i vtable. Jest to jednak szybsze, ponieważ brak maszyny wirtualnej nie pozwala na przechwycenie metody środowiska wykonawczego. To przechwycenie jest podstawową częścią kakao i jest wymagane dla następujących typów funkcji:

  • Eleganccy obserwatorzy nieruchomości Cocoa. (Obserwatorzy nieruchomości są wypiekani bezpośrednio w języku Swift).
  • Nieinwazyjne stosowanie zagadnień przekrojowych, takich jak rejestrowanie, zarządzanie transakcjami (tj. Programowanie zorientowane na aspekt).
  • proxy, przekazywanie wiadomości, itp

Dlatego zaleca się, aby jego clases w aplikacjach Cocoa/Cocoa Touch realizowanych ze SWIFT:

  • Extend z NSObject. Okno dialogowe nowej klasy w Xcode kieruje w tym kierunku.
  • W przypadku, gdy narzut dynamicznej wysyłki prowadzi do problemów z wydajnością, można użyć wysyłania statycznego - na przykład w ciasnych pętlach z wywołaniami metod o bardzo małych ciałach.

Podsumowanie:

  • Swift może zachowywać się jak C++, z szybkim statycznym/vtable wysyłki i ograniczoną refleksji. Dzięki temu nadaje się do aplikacji wymagających niższego poziomu lub dużej mocy obliczeniowej, 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 powiadamiania o wywołaniu metody dodaje introspekcji i dynamizmowi języki takie jak Ruby i Python, podobnie jak Objective-C, ale bez składni Spójrz na C-a.

referencyjny dane: Wykonanie napowietrzne dla metody inwokacji:

  • statyczne: < 1.1ns
  • vtable: ~ 1.1ns
  • dynamiczne: ~ 4.9ns

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

Ponadto atrybut dynamiczny pozwala nam wyraźnie polecić Swiftowi, że metoda powinna korzystać z dynamicznej wysyłki, a zatem będzie wspierać przechwytywanie.

public dynamic func foobar() -> AnyObject { 
} 
+1

Nawet przy użyciu technik Objective-C wydaje się nie działać dla opcjonalnych typów Swift. Proponuję zauważyć to ograniczenie w odpowiedzi, chyba że brakuje mi jakiejś sztuczki. – Lee

+0

@LeeWhitney, edytuj odpowiedź. –

6

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

Od wersji beta 6 _stdlib_getTypeName otrzymuje nazwę zmienną typu "zniekształcona". Pasta ta w pustym 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))") 

wyjście jest:

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

Ewan Swick's blog entry pomaga rozwiązać te łańcuchy:

np _TtSi oznacza wewnętrzny typ Swifta: Int.

Mike Ash has a great blog entry covering the same topic.

+5

'_stdlib_getDemangledTypeName' może być również użyty – aleclarson

+0

@aleclarson Tak, jest to również przydatne. – Klaas

+1

nie są tymi prywatnymi API? Czy Apple zatwierdzi aplikację, jeśli będzie używana? –

5

Można rozważyć użycie zamiast tego toString(). Jest on publiczny i działa tak samo, jak _stdlib_getTypeName() z tą różnicą, że działa również na AnyClass, np. na placu zabaw wpisz

class MyClass {} 

toString(MyClass.self) // evaluates to "__lldb_expr_49.MyClass" 
Powiązane problemy