Apakah Swift mendukung refleksi?

113

Apakah Swift mendukung refleksi? misalnya apakah ada sesuatu seperti valueForKeyPath:dan setValue:forKeyPath:untuk objek Swift?

Sebenarnya apakah itu bahkan memiliki sistem tipe dinamis, seperti obj.classdi Objective-C?

Khanh Nguyen
sumber
1
Saya membuat kelas pembantu untuk refleksi di Swift. Anda dapat menemukannya di: github.com/evermeer/EVReflection
Edwin Vermeer
2
Mereka telah menghapus refleksi dalam Swift 2.0. Ini adalah cara saya menghitung atribut dan nilai. Link
mohacs

Jawaban:

85

Sepertinya ada awal dari beberapa dukungan refleksi:

class Fruit {
    var name="Apple"
}

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

Dari mchambers gist, di sini: https://gist.github.com/mchambers/fb9da554898dae3e54f2

stevex.dll
sumber
5
Yah, saya tidak akan menganggap ini sebagai refleksi yang nyata. Untuk satu hal, itu hanya untuk dibaca. Menurut saya ini hanya peretasan untuk mengaktifkan debugging di Xcode. Protokol Mirrorsebenarnya mengutip kata tersebut IDEbeberapa kali.
Sulthan
7
Dan itu hanya berfungsi untuk properti. Tidak ada refleksi metode.
Sulthan
11
Penulis inti dari check in. Saya menulis ini di laboratorium Swift di WWDC, berpikir saya akan membagikan sisanya. Seperti yang diketahui semua orang, teknisi yang saya ajak bicara mengonfirmasi bahwa fungsi reflect () ada untuk mendukung Playground. Tapi Anda masih bisa bersenang-senang dengan itu :) di sini saya telah meretas model serializer kecil yang menggunakannya. Tempel ke Playground dan bersenang-senanglah: gist.github.com/mchambers/67640d9c3e2bcffbb1e2
Marc Chambers
1
Lihat jawaban stackoverflow.com/a/25345461/292145 untuk mengetahui bagaimana _stdlib_getTypeNamedapat membantu.
Klaas
1
Berikut adalah kelas yang akan melakukan refleksi kelas dasar dan opsional (bukan tipe) dan memiliki dukungan untuk NSCoding dan parsing dari dan ke kamus: github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/…
Edwin Vermeer
44

Jika sebuah kelas diperluas NSObject, maka semua introspeksi dan dinamisme Objective-C akan berfungsi. Ini termasuk:

  • Kemampuan untuk menanyakan kelas tentang metode dan propertinya, dan untuk memanggil metode atau menyetel properti.
  • Kemampuan untuk bertukar implementasi metode. (tambahkan fungsionalitas ke semua contoh).
  • Kemampuan untuk menghasilkan dan menetapkan sub-kelas baru dengan cepat. (tambahkan fungsionalitas ke instance tertentu)

Satu kekurangan dari fungsionalitas ini adalah dukungan untuk tipe nilai opsional Swift. Misalnya properti Int dapat dihitung dan dimodifikasi tetapi Int? properti tidak bisa. Jenis opsional dapat disebutkan sebagian menggunakan reflect / MirrorType, tetapi masih tidak dimodifikasi.

Jika sebuah kelas tidak diperluas NSObject, maka hanya refleksi baru yang sangat terbatas (dan sedang dalam proses?) Yang berfungsi (lihat reflect / MirrorType), yang menambahkan kemampuan terbatas untuk menanyakan instance tentang kelas dan propertinya, tetapi tidak ada fitur tambahan di atas .

Saat tidak memperluas NSObject, atau menggunakan direktif '@objc', Swift secara default menggunakan pengiriman berbasis statis dan vtable. Ini lebih cepat, bagaimanapun, dengan tidak adanya mesin virtual tidak memungkinkan intersepsi metode runtime. Intersepsi ini merupakan bagian fundamental dari Kakao dan diperlukan untuk jenis fitur berikut:

  • Pengamat properti yang elegan dari Cocoa. (Pengamat properti terbiasa dengan bahasa Swift).
  • Penerapan masalah lintas sektor yang tidak invasif seperti penebangan, manajemen transaksi (yaitu Pemrograman Berorientasi Aspek).
  • Proksi, penerusan pesan, dll.

Oleh karena itu, disarankan agar klas dalam aplikasi Cocoa / CocoaTouch diimplementasikan dengan Swift:

  • Perpanjang dari NSObject. Dialog kelas baru di Xcode mengarahkan ke arah ini.
  • Jika overhead pengiriman dinamis menyebabkan masalah kinerja, maka pengiriman statis dapat digunakan - dalam loop ketat dengan panggilan ke metode dengan badan yang sangat kecil, misalnya.

Ringkasan:

  • Swift dapat berperilaku seperti C ++, dengan pengiriman statis / vtable cepat dan refleksi terbatas. Ini membuatnya cocok untuk aplikasi tingkat rendah atau kinerja intensif, tetapi tanpa kerumitan, kurva pembelajaran atau risiko kesalahan yang terkait dengan C ++
  • Meskipun Swift adalah bahasa yang dikompilasi, gaya pesan pemanggilan metode menambahkan introspeksi dan dinamisme yang ditemukan dalam bahasa modern seperti Ruby dan Python, seperti Objective-C, tetapi tanpa sintaks lama Objective-C.

Data referensi: Overhead eksekusi untuk pemanggilan metode:

  • statis: <1.1ns
  • vtable: ~ 1.1ns
  • dinamis: ~ 4.9ns

(kinerja sebenarnya bergantung pada perangkat keras, tetapi rasionya akan tetap sama).

Selain itu, atribut dynamic memungkinkan kita untuk secara eksplisit menginstruksikan Swift bahwa suatu metode harus menggunakan pengiriman dinamis, dan oleh karena itu akan mendukung intersepsi.

public dynamic func foobar() -> AnyObject {
}
Jasper Blues
sumber
2
Bahkan menggunakan teknik Objective-C tampaknya tidak berfungsi untuk tipe Swift opsional. Saya akan menyarankan untuk mencatat batasan ini dalam jawaban kecuali saya melewatkan trik.
whitneyland
8

Dokumentasi berbicara tentang sistem tipe dinamis, terutama tentang

Type dan dynamicType

Lihat Jenis Metatype (dalam Referensi Bahasa)

Contoh:

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

var type = instance.dynamicType

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

Sekarang dengan asumsi TestObjectmeluasNSObject

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

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

Saat ini, belum ada refleksi yang dilaksanakan.

EDIT: Saya ternyata salah, lihat jawaban stevex. Ada beberapa refleksi sederhana hanya baca untuk properti yang dibangun, mungkin untuk memungkinkan IDE memeriksa konten objek.

Sulthan
sumber
6

Tampaknya API refleksi Swift saat ini bukan prioritas utama Apple. Tapi selain jawaban @stevex ada fungsi lain di perpustakaan standar yang membantu.

Pada beta 6 _stdlib_getTypeNamemendapatkan nama jenis variabel yang rusak. Rekatkan ini ke taman bermain kosong:

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

Outputnya adalah:

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

Entri blog Ewan Swick membantu menguraikan string berikut:

misalnya _TtSisingkatan dari internal SwiftInt tipe .

Mike Ash memiliki entri blog yang bagus yang membahas topik yang sama .

Klaas
sumber
@aleclarson Ya, itu juga cukup berguna.
Klaas
1
bukankah itu API pribadi? Akankah Apple menyetujui aplikasi jika digunakan?
Eduardo Costa
@EduardoCosta ya, pasti. Mereka pribadi. Saya hanya menggunakannya untuk debug build.
Klaas
Berikut tautan yang diperbarui ke artikel blog Ewan Swick: eswick.com/2014/06/08/Inside-Swift
RenniePet
5

Anda mungkin ingin mempertimbangkan untuk menggunakan toString () sebagai gantinya. Ini publik dan berfungsi sama seperti _stdlib_getTypeName () dengan perbedaan bahwa itu juga berfungsi di AnyClass , misalnya di Playground enter

class MyClass {}

toString(MyClass.self) // evaluates to "__lldb_expr_49.MyClass"
silkentrance
sumber
1

Tidak ada reflectkata kunci di Swift 5, sekarang Anda dapat menggunakan

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
sumber
Mengapa ini tidak diberi suara positif? Ini sangat berguna. Saya akan menerapkannya untuk jsondeserialisasi
javadba