Swift có hỗ trợ phản chiếu không?


113

Swift có hỗ trợ phản chiếu không? ví dụ: có một cái gì đó giống như valueForKeyPath:setValue:forKeyPath:cho các đối tượng Swift?

Trên thực tế, nó thậm chí có một hệ thống kiểu động, giống như obj.classtrong Objective-C?


1
Tôi đã tạo một lớp trợ giúp để phản ánh trong Swift. Bạn có thể tìm thấy nó tại: github.com/evermeer/EVReflection
Edwin Vermeer

2
Họ đã loại bỏ phản ánh trong Swift 2.0. Đây là cách tôi liệt kê các thuộc tính và các giá trị liên kết
mohacs

Câu trả lời:


85

Có vẻ như có một số hỗ trợ phản ánh bắt đầu:

class Fruit {
    var name="Apple"
}

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

Từ ý chính của mchambers, tại đây: https://gist.github.com/mchambers/fb9da554898dae3e54f2


5
Chà, tôi sẽ không coi đây là một phản ánh thực sự. Đối với một điều, nó chỉ đọc. Đối với tôi, có vẻ như đó chỉ là một hack để kích hoạt gỡ lỗi trong Xcode. Giao thức Mirrorthực sự trích dẫn từ IDEnhiều lần.
Sulthan

7
Và nó chỉ hoạt động cho các thuộc tính. Không phản ánh phương pháp.
Sulthan

11
Tác giả của bài kiểm tra ý chính. Tôi đã viết bài này trong phòng thí nghiệm Swift tại WWDC, tôi nghĩ rằng tôi sẽ chia sẻ phần còn lại của nó. Như mọi người đã tìm ra, các kỹ sư mà tôi đã nói chuyện cùng đã xác nhận rằng hàm phản xạ () tồn tại để hỗ trợ Playground. Nhưng bạn vẫn có thể vui vẻ với nó :) ở đây tôi đã hack một bộ tuần tự kiểu nhỏ bằng cách sử dụng nó. Dán nó vào trong sân chơi và vui chơi: gist.github.com/mchambers/67640d9c3e2bcffbb1e2
Marc Chambers

1
Hãy xem câu trả lời stackoverflow.com/a/25345461/292145 để tìm hiểu cách _stdlib_getTypeNamecó thể trợ giúp.
Klaas

1
Đây là một lớp sẽ phản ánh các lớp cơ sở và tùy chọn (không phải loại) và có hỗ trợ NSCoding và phân tích cú pháp từ và đến từ điển: github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/…
Edwin Vermeer

44

Nếu một lớp mở rộng NSObject, thì tất cả tính năng động và nội tâm của Objective-C sẽ hoạt động. Điêu nay bao gôm:

  • Khả năng hỏi một lớp về các phương thức và thuộc tính của nó cũng như gọi các phương thức hoặc thiết lập thuộc tính.
  • Khả năng trao đổi việc triển khai phương pháp. (thêm chức năng cho tất cả các trường hợp).
  • Khả năng tạo và chỉ định một lớp con mới một cách nhanh chóng. (thêm chức năng vào một phiên bản nhất định)

Một thiếu sót của chức năng này là hỗ trợ các kiểu giá trị tùy chọn của Swift. Ví dụ, thuộc tính Int có thể được liệt kê và sửa đổi nhưng Int? thuộc tính không thể. Các loại tùy chọn có thể được liệt kê một phần bằng cách sử dụng phản xạ / MirrorType, nhưng vẫn không được sửa đổi.

Nếu một lớp không mở rộng NSObject, thì chỉ phản xạ mới, rất hạn chế (và đang xử lý?) Mới hoạt động (xem phản xạ / MirrorType), bổ sung khả năng hạn chế để hỏi một trường hợp về lớp và thuộc tính của nó, nhưng không có tính năng bổ sung nào ở trên .

Khi không mở rộng NSObject, hoặc sử dụng chỉ thị '@objc', Swift mặc định là gửi tĩnh và dựa trên vtable. Điều này nhanh hơn, tuy nhiên, trong trường hợp không có máy ảo không cho phép chặn phương thức thời gian chạy. Việc đánh chặn này là một phần cơ bản của Cacao và cần thiết cho các loại tính năng sau:

  • Những người quan sát tài sản thanh lịch của Cocoa. (Các nhà quan sát tài sản được sử dụng ngay trong ngôn ngữ Swift).
  • Không xâm phạm áp dụng các mối quan tâm xuyên suốt như ghi nhật ký, quản lý giao dịch (tức là Lập trình hướng theo khía cạnh).
  • Proxy, chuyển tiếp tin nhắn, v.v.

Do đó, nó được khuyến nghị rằng clases trong các ứng dụng Cocoa / CocoaTouch được triển khai với Swift:

  • Mở rộng từ NSObject. Hộp thoại lớp mới trong Xcode hướng theo hướng này.
  • Trong trường hợp chi phí của một điều phối động dẫn đến các vấn đề về hiệu suất, thì điều phối tĩnh có thể được sử dụng - trong các vòng lặp chặt chẽ với các lệnh gọi đến các phương thức có nội dung rất nhỏ, chẳng hạn.

Tóm lược:

  • Swift có thể hoạt động giống như C ++, với tốc độ gửi tĩnh / vtable nhanh chóng và phản xạ hạn chế. Điều này làm cho nó phù hợp với các ứng dụng chuyên sâu về hiệu suất hoặc cấp thấp hơn, nhưng không có sự phức tạp, đường cong học tập hoặc nguy cơ lỗi liên quan đến C ++
  • Mặc dù Swift là một ngôn ngữ biên dịch, nhưng kiểu nhắn tin của phương thức gọi bổ sung thêm tính năng động và nội tâm được tìm thấy trong các ngôn ngữ hiện đại như Ruby và Python, giống như Objective-C, nhưng không có cú pháp kế thừa của Objective-C.

Dữ liệu tham khảo: Chi phí thực thi cho các lệnh gọi phương thức:

  • tĩnh: <1,1ns
  • vtable: ~ 1.1ns
  • động: ~ 4,9ns

(hiệu suất thực tế phụ thuộc vào phần cứng, nhưng tỷ lệ sẽ vẫn tương tự).

Ngoài ra, thuộc tính động cho phép chúng tôi chỉ dẫn rõ ràng cho Swift rằng một phương thức nên sử dụng điều phối động và do đó sẽ hỗ trợ việc đánh chặn.

public dynamic func foobar() -> AnyObject {
}

2
Ngay cả khi sử dụng các kỹ thuật Objective-C, nó dường như không hoạt động đối với các kiểu Swift tùy chọn. Tôi đề nghị lưu ý hạn chế này trong câu trả lời trừ khi tôi thiếu một mẹo nhỏ.
whitneyland

8

Tài liệu nói về hệ thống kiểu động, chủ yếu là về

TypedynamicType

Xem Loại Metatype (trong Tham chiếu Ngôn ngữ)

Thí dụ:

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

var type = instance.dynamicType

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

Bây giờ giả sử TestObjectmở rộngNSObject

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

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

Hiện tại, chưa có phản ánh nào được thực hiện.

CHỈNH SỬA: Tôi dường như đã sai, hãy xem câu trả lời của stevex. Có một số phản ánh chỉ đọc đơn giản cho các thuộc tính được tích hợp, có thể là để cho phép các IDE kiểm tra nội dung đối tượng.


6

Có vẻ như API phản chiếu Swift không phải là ưu tiên cao của Apple vào lúc này. Nhưng bên cạnh câu trả lời @stevex, có một chức năng khác trong thư viện tiêu chuẩn giúp ích.

Kể từ phiên bản beta 6, _stdlib_getTypeNamecó tên kiểu bị xáo trộn của một biến. Dán cái này vào một sân chơi trống:

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

Đầu ra là:

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

Mục nhập blog của Ewan Swick giúp giải mã các chuỗi này:

ví dụ: _TtSiviết tắt của Intkiểu nội bộ của Swift .

Mike Ash có một mục blog tuyệt vời về chủ đề tương tự .


@aleclarson Vâng, điều đó cũng khá hữu ích.
Klaas

1
đó không phải là API riêng? Liệu Apple có chấp thuận ứng dụng nếu chúng được sử dụng?
Eduardo Costa

@EduardoCosta có, chắc chắn rồi. Chúng là riêng tư. Tôi chỉ sử dụng chúng cho các bản dựng gỡ lỗi.
Klaas

Đây là liên kết cập nhật tới bài viết trên blog của Ewan Swick: eswick.com/2014/06/08/Inside-Swift
RenniePet

5

Bạn có thể muốn xem xét sử dụng toString () để thay thế. Nó là công khai và hoạt động giống như _stdlib_getTypeName () với sự khác biệt là nó cũng hoạt động trên AnyClass , ví dụ: trong Playground enter

class MyClass {}

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

1

Không có reflecttừ khóa nào trong Swift 5, bây giờ bạn có thể sử dụng

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

Tại sao điều này không được ủng hộ? Điều này là hữu ích v. Tôi sẽ áp dụng nó cho quá trình jsondeserialization
javadba
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.