Swift รองรับการสะท้อนหรือไม่?


113

Swift รองรับการสะท้อนหรือไม่? เช่นมีบางอย่างเช่นvalueForKeyPath:และsetValue:forKeyPath:สำหรับวัตถุ Swift หรือไม่?

จริงๆแล้วมันมีระบบประเภทไดนามิกobj.classหรือไม่เช่นใน Objective-C หรือไม่?


1
ฉันสร้างคลาสตัวช่วยสำหรับการสะท้อนใน Swift คุณสามารถค้นหาได้ที่: github.com/evermeer/EVReflection
Edwin Vermeer

2
พวกเขาได้ลบการสะท้อนภายใน Swift 2.0 นี่คือวิธีที่ฉันกำลังแจกแจงแอตทริบิวต์และค่าต่างๆLink
mohacs

คำตอบ:


85

ดูเหมือนว่ามีจุดเริ่มต้นของการสนับสนุนการสะท้อนกลับ:

class Fruit {
    var name="Apple"
}

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

จากส่วนสำคัญของ mchambers ที่นี่: https://gist.github.com/mchambers/fb9da554898dae3e54f2


5
ฉันจะไม่คิดว่านี่เป็นภาพสะท้อนที่แท้จริง ประการหนึ่งคืออ่านอย่างเดียว สำหรับฉันแล้วดูเหมือนว่าเป็นเพียงการแฮ็กเพื่อเปิดใช้งานการดีบักใน Xcode Mirrorจริงๆแล้วพิธีสารจะพูดคำนี้IDEหลายครั้ง
Sulthan

7
และใช้ได้กับคุณสมบัติเท่านั้น ไม่มีการสะท้อนวิธี
Sulthan

11
ผู้เขียนเช็คอินที่สำคัญฉันเขียนสิ่งนี้ในห้องปฏิบัติการ Swift ที่ WWDC คิดว่าฉันจะแบ่งปันส่วนที่เหลือ ตามที่ทุกคนคิดออกวิศวกรที่ฉันพูดด้วยยืนยันว่ามีฟังก์ชัน reflect () เพื่อรองรับ Playground แต่คุณยังสามารถสนุกกับมันได้ :) ที่นี่ฉันได้แฮ็ก serializer รุ่นเล็ก ๆ โดยใช้มัน วางลงใน Playground และสนุก: gist.github.com/mchambers/67640d9c3e2bcffbb1e2
Marc Chambers

1
ลองดูคำตอบstackoverflow.com/a/25345461/292145เพื่อดูว่า_stdlib_getTypeNameจะช่วยได้อย่างไร
Klaas

1
นี่คือคลาสที่จะสะท้อนคลาสพื้นฐานและตัวเลือก (ไม่ใช่ประเภท) และรองรับ NSCoding และการแยกวิเคราะห์จากและไปยังพจนานุกรม: github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/…
Edwin Vermeer

44

ถ้าคลาสขยายออกไปNSObjectการวิปัสสนาและพลวัตของ Objective-C ทั้งหมดจะทำงาน ซึ่งรวมถึง:

  • ความสามารถในการถามคลาสเกี่ยวกับเมธอดและคุณสมบัติและเรียกใช้เมธอดหรือตั้งค่าคุณสมบัติ
  • ความสามารถในการแลกเปลี่ยนวิธีการใช้งาน (เพิ่มฟังก์ชันให้กับอินสแตนซ์ทั้งหมด)
  • ความสามารถในการสร้างและกำหนดคลาสย่อยใหม่ได้ทันที (เพิ่มฟังก์ชันให้กับอินสแตนซ์ที่กำหนด)

ข้อบกพร่องประการหนึ่งของฟังก์ชันนี้คือการรองรับประเภทค่าที่เป็นทางเลือกของ Swift ตัวอย่างเช่นคุณสมบัติ Int สามารถแจกแจงและแก้ไขได้ แต่ Int? คุณสมบัติไม่ได้ สามารถระบุประเภทที่เป็นทางเลือกได้บางส่วนโดยใช้ reflect / MirrorType แต่ยังไม่ได้แก้ไข

หากคลาสไม่ขยายออกNSObjectจะมีเพียงการสะท้อนแบบใหม่ที่ จำกัด มาก (และกำลังดำเนินการ?) เท่านั้นที่ทำงานได้ (ดู reflect / MirrorType) ซึ่งจะเพิ่มความสามารถที่ จำกัด ในการถามอินสแตนซ์เกี่ยวกับคลาสและคุณสมบัติของคลาส แต่ไม่มีคุณสมบัติเพิ่มเติมใด ๆ ข้างต้น .

เมื่อไม่ขยาย NSObject หรือใช้คำสั่ง '@objc' Swift จะตั้งค่าเริ่มต้นเป็นการจัดส่งแบบคงที่และแบบ vtable เร็วกว่าอย่างไรก็ตามในกรณีที่ไม่มีเครื่องเสมือนไม่อนุญาตให้มีการสกัดกั้นเมธอดรันไทม์ การสกัดกั้นนี้เป็นส่วนพื้นฐานของโกโก้และจำเป็นสำหรับคุณสมบัติประเภทต่อไปนี้:

  • ผู้สังเกตการณ์ทรัพย์สินที่สง่างามของโกโก้ (ผู้สังเกตการณ์คุณสมบัติถูกนำมาใช้ในภาษา Swift)
  • การใช้ข้อกังวลข้ามการตัดแบบไม่รุกรานเช่นการบันทึกการจัดการธุรกรรม (เช่น Aspect Oriented Programming)
  • พร็อกซีการส่งต่อข้อความ ฯลฯ

ดังนั้นจึงแนะนำให้ใช้ clases ใน Cocoa / CocoaTouch กับ Swift:

  • ขยายความจาก NSObject กล่องโต้ตอบคลาสใหม่ใน Xcode นำไปในทิศทางนี้
  • ในกรณีที่ค่าใช้จ่ายของการจัดส่งแบบไดนามิกนำไปสู่ปัญหาด้านประสิทธิภาพจึงสามารถใช้การจัดส่งแบบคงที่ได้เช่นในลูปที่แน่นด้วยการเรียกใช้เมธอดที่มีเนื้อความขนาดเล็กมาก

สรุป:

  • Swift สามารถทำงานเหมือน C ++ โดยมีการจัดส่งแบบคงที่ / vtable อย่างรวดเร็วและการสะท้อนที่ จำกัด ทำให้เหมาะสำหรับแอปพลิเคชันระดับล่างหรือประสิทธิภาพสูง แต่ไม่มีความซับซ้อนเส้นโค้งการเรียนรู้หรือความเสี่ยงต่อข้อผิดพลาดที่เกี่ยวข้องกับ C ++
  • ในขณะที่ Swift เป็นภาษาที่คอมไพล์แล้วรูปแบบการส่งข้อความของการเรียกใช้เมธอดจะเพิ่มการหยั่งรู้และพลวัตที่พบในภาษาสมัยใหม่เช่น Ruby และ Python เช่นเดียวกับ Objective-C แต่ไม่มีไวยากรณ์ดั้งเดิมของ Objective-C

ข้อมูลอ้างอิง: ค่าใช้จ่ายในการดำเนินการสำหรับการเรียกใช้เมธอด:

  • คงที่: <1.1ns
  • vtable: ~ 1.1ns
  • ไดนามิก: ~ 4.9ns

(ประสิทธิภาพจริงขึ้นอยู่กับฮาร์ดแวร์ แต่อัตราส่วนจะยังคงใกล้เคียงกัน)

นอกจากนี้แอตทริบิวต์ไดนามิกยังช่วยให้เราสามารถสั่ง Swift ได้อย่างชัดเจนว่าเมธอดควรใช้การจัดส่งแบบไดนามิกดังนั้นจึงรองรับการสกัดกั้น

public dynamic func foobar() -> AnyObject {
}

2
แม้จะใช้เทคนิค Objective-C ดูเหมือนว่าจะใช้ไม่ได้กับประเภท Swift ที่เป็นทางเลือก ฉันขอแนะนำให้สังเกตข้อ จำกัด นี้ในคำตอบเว้นแต่ฉันจะพลาดเคล็ดลับ
แลนด์

8

เอกสารนี้พูดเกี่ยวกับระบบประเภทไดนามิกส่วนใหญ่เกี่ยวกับ

Type และ dynamicType

ดูประเภท Metatype (ในการอ้างอิงภาษา)

ตัวอย่าง:

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

var type = instance.dynamicType

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

ตอนนี้สมมติว่าTestObjectขยายNSObject

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

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

ปัจจุบันไม่มีการสะท้อนกลับมาใช้

แก้ไข:เห็นได้ชัดว่าฉันคิดผิดดูคำตอบของ stevex มีการสะท้อนแบบอ่านอย่างเดียวง่ายๆสำหรับคุณสมบัติที่สร้างขึ้นซึ่งอาจเพื่อให้ IDEs ตรวจสอบเนื้อหาวัตถุ


6

ดูเหมือนว่า API การสะท้อนของ Swift จะไม่ใช่สิ่งสำคัญสำหรับ Apple ในขณะนี้ แต่นอกจากคำตอบ @stevex แล้วยังมีฟังก์ชันอื่นในไลบรารีมาตรฐานที่ช่วยได้

เมื่อเบต้า 6 _stdlib_getTypeNameได้รับชื่อชนิดของตัวแปร วางสิ่งนี้ลงในสนามเด็กเล่นที่ว่างเปล่า:

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

ผลลัพธ์คือ:

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

รายการบล็อกของ Ewan Swickช่วยในการถอดรหัสสตริงเหล่านี้:

เช่น_TtSiย่อมาจากIntประเภทภายในของ Swift

ไมค์แอชมีรายการบล็อกที่ดีครอบคลุมหัวข้อเดียวกัน


@aleclarson ใช่นั่นก็มีประโยชน์เช่นกัน
Klaas

1
API ส่วนตัวเหล่านั้นไม่ใช่หรือ Apple จะอนุมัติแอปหรือไม่หากมีการใช้งาน
Eduardo Costa

@EduardoCosta ใช่แน่นอน เป็นส่วนตัว ฉันใช้มันสำหรับการสร้างดีบักเท่านั้น
Klaas

นี่คือลิงค์ที่อัปเดตไปยังบทความบล็อกของ Ewan Swick: eswick.com/2014/06/08/Inside-Swift
RenniePet

5

คุณอาจต้องการพิจารณาใช้toString ()แทน เป็นสาธารณะและใช้งานได้เหมือนกับ_stdlib_getTypeName ()โดยมีข้อแตกต่างที่ใช้งานได้กับAnyClassเช่นใน Playground enter

class MyClass {}

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

1

ไม่มีreflectคีย์เวิร์ดใน Swift 5 ตอนนี้คุณสามารถใช้ได้

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

เหตุใดจึงไม่โหวตให้คะแนน สิ่งนี้มีประโยชน์ ฉันจะนำไปใช้สำหรับjsondeserialization
javadba
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.