Mặc dù tôi nghĩ rằng tôi có thể trả lời câu hỏi của bạn, nhưng đó không phải là câu trả lời mà bạn sẽ thích.
TL; DR: các @objc
chức năng hiện có thể không có trong phần mở rộng giao thức. Thay vào đó, bạn có thể tạo một lớp cơ sở, mặc dù đó không phải là một giải pháp lý tưởng.
Phần mở rộng Giao thức và Mục tiêu-C
Đầu tiên, câu hỏi / câu trả lời này ( Phương pháp Swift có thể được xác định trên tiện ích mở rộng trên giao thức được truy cập trong Objective-c ) dường như gợi ý rằng do cách thức mà tiện ích mở rộng giao thức được gửi đi, các phương thức được khai báo trong tiện ích mở rộng giao thức không hiển thị đối với objc_msgSend()
hàm và do đó không hiển thị đối với mã Objective-C. Vì phương thức bạn đang cố gắng xác định trong tiện ích mở rộng của mình cần phải hiển thị với Objective-C (vì vậy UIKit
có thể sử dụng nó), nó la mắng bạn vì không bao gồm @objc
, nhưng một khi bạn bao gồm nó, nó sẽ la mắng bạn vì @objc
không được phép trong phần mở rộng giao thức. Điều này có thể là do các phần mở rộng giao thức hiện không thể hiển thị cho Objective-C.
Chúng tôi cũng có thể thấy rằng thông báo lỗi khi chúng tôi thêm @objc
trạng thái "@objc chỉ có thể được sử dụng với các thành viên của các lớp, giao thức @objc và phần mở rộng cụ thể của các lớp". Đây không phải là một lớp học; phần mở rộng cho giao thức @objc không giống như trong chính định nghĩa giao thức (nghĩa là trong các yêu cầu) và từ "cụ thể" sẽ gợi ý rằng phần mở rộng giao thức không được tính là phần mở rộng lớp cụ thể.
Cách giải quyết
Thật không may, điều này hoàn toàn ngăn cản bạn sử dụng các phần mở rộng giao thức khi các triển khai mặc định phải hiển thị cho các khung Objective-C. Lúc đầu, tôi nghĩ có lẽ @objc
không được phép trong phần mở rộng giao thức của bạn vì Trình biên dịch Swift không thể đảm bảo rằng các kiểu tuân thủ sẽ là các lớp (mặc dù bạn đã chỉ định cụ thể UIViewController
). Vì vậy, tôi đặt ra một class
yêu cầu P1
. Điều này đã không hoạt động.
Có lẽ cách giải quyết duy nhất là chỉ cần sử dụng một lớp cơ sở thay vì một giao thức ở đây, nhưng điều này rõ ràng không hoàn toàn lý tưởng vì một lớp có thể chỉ có một lớp cơ sở duy nhất nhưng phù hợp với nhiều giao thức.
Nếu bạn chọn đi theo con đường này, hãy tính đến câu hỏi này ( Phương thức giao thức tùy chọn Swift 3 ObjC Không được gọi trong Lớp con ). Có vẻ như một vấn đề hiện tại khác trong Swift 3 là các lớp con không tự động kế thừa các triển khai yêu cầu giao thức tùy chọn của lớp cha của chúng. Câu trả lời cho những câu hỏi đó sử dụng một sự thích nghi đặc biệt @objc
để vượt qua nó.
Báo cáo vấn đề
Tôi nghĩ rằng điều này đang được thảo luận giữa những người làm việc trong các dự án mã nguồn mở Swift, nhưng bạn có thể chắc chắn rằng họ biết được bằng cách sử dụng Trình báo lỗi của Apple , điều này có khả năng cuối cùng sẽ được chuyển đến Nhóm Swift Core hoặc báo cáo lỗi của Swift . Tuy nhiên, một trong hai lỗi này có thể thấy lỗi của bạn quá rộng hoặc đã biết. Nhóm Swift cũng có thể coi những gì bạn đang tìm kiếm là một tính năng ngôn ngữ mới, trong trường hợp đó, trước tiên bạn nên kiểm tra danh sách gửi thư .
Cập nhật
Vào tháng 12 năm 2016, vấn đề này đã được báo cáo cho cộng đồng Swift. Vấn đề vẫn được đánh dấu là mở với mức độ ưu tiên trung bình, nhưng nhận xét sau đã được thêm vào:
Đây là dự định. Không có cách nào để thêm việc triển khai phương pháp cho mọi người dùng, vì phần mở rộng có thể được thêm vào sau khi tuân thủ giao thức. Tuy nhiên, tôi cho rằng chúng ta có thể cho phép nó nếu tiện ích mở rộng nằm trong cùng một mô-đun với giao thức.
Tuy nhiên, vì giao thức của bạn nằm trong cùng một mô-đun với tiện ích mở rộng của bạn, bạn có thể thực hiện việc này trong phiên bản Swift trong tương lai.
Cập nhật 2
Vào tháng 2 năm 2017, vấn đề này đã chính thức bị đóng lại là "Sẽ không làm" bởi một trong những thành viên Nhóm Swift Core với thông báo sau:
Điều này là cố ý: các phần mở rộng giao thức không thể giới thiệu các điểm nhập @objc do các hạn chế của thời gian chạy Objective-C. Nếu bạn muốn thêm điểm nhập @objc vào NSObject, hãy mở rộng NSObject.
Việc mở rộng NSObject
hoặc thậm chí UIViewController
sẽ không đạt được chính xác những gì bạn muốn, nhưng tiếc là nó không giống như nó sẽ trở thành khả thi.
Trong tương lai (rất) dài hạn, chúng ta có thể loại bỏ @objc
hoàn toàn sự phụ thuộc vào các phương pháp, nhưng thời điểm đó có thể sẽ không đến sớm vì các khuôn khổ Cocoa hiện không được viết bằng Swift (và không thể cho đến khi nó có ABI ổn định) .
Cập nhật 3
Kể từ mùa thu năm 2019, điều này đang trở nên ít vấn đề hơn vì ngày càng có nhiều khuôn khổ Apple được viết bằng Swift. Ví dụ, nếu bạn sử dụng SwiftUI
thay vì UIKit
, bạn hoàn toàn bỏ qua vấn đề vì @objc
sẽ không bao giờ cần thiết khi đề cập đến một SwiftUI
phương pháp.
Các khung của Apple được viết bằng Swift bao gồm:
- SwiftUI
- RealityKit
- Phối hợp
- CryptoKit
Người ta mong đợi mô hình này sẽ tiếp tục theo thời gian khi Swift chính thức có ABI và mô-đun ổn định như Swift 5.0 và 5.1, tương ứng.
@objc