2017-02-03 13 views
9

FYI: lỗi Swift lớn lên ở đây: https://bugs.swift.org/browse/SR-3871Không cast trong Swift từ Any? giao thức


Tôi đang gặp một vấn đề kỳ lạ nơi một dàn diễn viên không hoạt động, nhưng giao diện điều khiển cho thấy nó như đúng loại.

Tôi có một giao thức nào

public protocol MyProtocol { } 

Và tôi thực hiện điều này trong một module, với một phương pháp nào đó trả về một ví dụ.

internal struct MyStruct: MyProtocol { } 

public func make() -> MyProtocol { return MyStruct() } 

Sau đó, trong điều khiển quan điểm của tôi, tôi kích hoạt một segue với đối tượng đó là người gửi

let myStruct = make() 
self.performSegue(withIdentifier: "Bob", sender: myStruct) 

Tất cả tốt cho đến nay.

Sự cố ở phương thức prepare(for:sender:) của tôi.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 
    if segue.identifier == "Bob" { 
     if let instance = sender as? MyProtocol { 
      print("Yay") 
     } 
    } 
} 

Tuy nhiên, ví dụ cho MyProtocol luôn trả về nil.

Khi tôi chạy po sender as! MyProtocol trong bảng điều khiển, nó cho tôi lỗi Could not cast value of type '_SwiftValue' (0x1107c4c70) to 'MyProtocol' (0x1107c51c8). Tuy nhiên, po sender sẽ xuất ra một phiên bản Module.MyStruct hợp lệ.

Tại sao tác vụ này không hoạt động?

(Tôi đã quản lý để giải quyết nó bằng cách đấm bốc giao thức của tôi trong một cấu trúc, nhưng tôi muốn biết lý do tại sao nó không làm việc như là, và nếu có một cách tốt hơn để sửa chữa nó)

+0

chỉ cần đi ra ngoài trên một chi ở đây, nhưng không thay đổi tuyên bố nội bộ ở đây 'nội bộ cấu trúc MyStruct: MyProtocol {}' để 'công' thay đổi bất cứ điều gì? – Dennis

+0

@Dennis Nope :( – deanWombourne

Trả lời

13

Đây là lỗi khá kỳ lạ - có vẻ như nó xảy ra khi một cá thể đã được bắc cầu đến Obj-C bằng cách được đóng hộp trong một _SwiftValue và được nhập tĩnh là Any(?). Ví dụ sau đó không thể được đúc thành một giao thức nhất định mà nó tuân theo.

Theo Joe Groff trong các ý kiến ​​của bug report you filed:

Đây là một thể hiện của vị tướng "runtime đúc động không cầu nếu cần thiết để nối đến một giao thức" lỗi. Vì người gửi được xem là loại đối tượng _SwiftValue và chúng tôi đang cố gắng truy cập một giao thức mà nó không tuân theo, chúng tôi bỏ cuộc mà không cần cố gắng loại cầu nối.

Một ví dụ nhỏ hơn sẽ là:

protocol P {} 
struct S : P {} 

let s = S() 

let val : Any = s as AnyObject // bridge to Obj-C as a _SwiftValue. 

print(val as? P) // nil 

thật là thú vị đủ, đầu tiên đúc để AnyObject và sau đó đúc để giao thức xuất hiện để làm việc:

print(val as AnyObject as! P) // S() 

Vì vậy, có vẻ như tĩnh gõ nó như AnyObject làm cho Swift cũng kiểm tra loại cầu nối cho sự phù hợp của giao thức, cho phép diễn viên thành công. Lý do cho điều này, như được giải thích trong một nhận xét khác của Joe Groff, là:

Thời gian chạy có một số lỗi chỉ chuyển một số độ sâu nhất định, nhưng không phải sau khi thực hiện các chuyển đổi khác (vì vậy AnyObject -> bridge -> Protocol có thể làm việc, nhưng Any -> AnyObject -> bridge -> Protocol không). Nó nên để hoạt động, ở bất kỳ mức nào.

+2

Lỗi được nêu ở đây: https://bugs.swift.org/browse/SR-3871 – deanWombourne

1

Các vấn đề là sender phải đi qua thế giới Objective-C, nhưng Objective-C không biết về mối quan hệ giao thức/cấu trúc này, vì cả hai giao thức Swift và các cấu trúc Swift đều không nhìn thấy được. Thay vì một cấu trúc, sử dụng một lớp:

protocol MyProtocol {} 
class MyClass: MyProtocol { } 
func make() -> MyProtocol { return MyClass() } 

Bây giờ mọi thứ hoạt động như bạn mong đợi, bởi vì sender có thể sống và thở mạch lạc trong Objective-C thế giới.

+2

Tôi không nói rằng hành vi mà bạn đã cô lập là đúng.Các cấu trúc đang được đóng hộp, nhưng bạn muốn nghĩ rằng mối quan hệ của nó với giao thức sẽ tồn tại mà bạn có thể muốn gửi một lỗi với 'bugs.swift.org'. – matt

0

Đây là giải pháp của tôi. Tôi không muốn chỉ làm cho nó thành một class (lại: this answer) bởi vì giao thức của tôi đang được thực hiện bởi nhiều thư viện và tất cả họ sẽ phải nhớ để làm điều đó.

Tôi đã đi đấm bốc giao thức của tôi thành một cấu trúc.

public struct BoxedMyProtocol: MyProtocol { 
    private let boxed: MyProtocol 

    // Just forward methods in MyProtocol onto the boxed value 
    public func myProtocolMethod(someInput: String) -> String { 
     return self.boxed.myProtocolMethod(someInput) 
    } 
} 

Bây giờ, tôi chỉ duyệt qua các trường hợp của BoxedMyProtocol.

Các vấn đề liên quan