Phương thức non - '@ objc' không đáp ứng yêu cầu tùy chọn của giao thức '@objc'


103

Tổng quat:

  • Tôi có một giao thức P1 cung cấp triển khai mặc định của một trong các chức năng tùy chọn Objective-C.
  • Khi tôi cung cấp triển khai mặc định của chức năng tùy chọn, có một cảnh báo

Cảnh báo trình biên dịch:

Non-'@objc' method 'presentationController(_:viewControllerForAdaptivePresentationStyle:)' does not satisfy optional requirement of '@objc' protocol 'UIAdaptivePresentationControllerDelegate'

Phiên bản:

  • Nhanh chóng: 3
  • Xcode: 8 (phát hành công khai)

Những cố gắng đã thực hiện:

  • Đã thử thêm @objcnhưng không giúp được gì

Câu hỏi:

  • Làm cách nào để giải quyết vấn đề này?
  • Có một công việc xung quanh?

Mã:

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {

}

extension P1 where Self : UIViewController {

    func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return UIViewController()
    }
}


class A : UIViewController, P1 {

}

Bạn có phiên bản Xcode mới nhất không? Tôi không gặp bất kỳ lỗi nào nếu tôi xóa@objc
Qbyte 14/09/2016

Tôi đang sử dụng Xcode 8 (phiên bản công khai mới nhất). Không có lỗi, nhưng sẽ có một cảnh báo
user1046037

Câu trả lời:


182

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 UIKitcó 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ì @objckhô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 @objctrạ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ẽ @objckhô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 classyê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 NSObjecthoặc thậm chí UIViewControllersẽ 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ỏ @objchoà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 SwiftUIthay vì UIKit, bạn hoàn toàn bỏ qua vấn đề vì @objcsẽ không bao giờ cần thiết khi đề cập đến một SwiftUIphươ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.


1
@ user1046037 Tôi cũng vậy, vì tôi có thể thấy mình gặp phải vấn đề này nhiều lần trong quá trình phát triển trong tương lai.
Matthew Seaman

2
Bạn đúng, câu trả lời của bạn vẫn tốt mặc dù Swift 4không có giải pháp thay thế nào khác cho đến thời điểm hiện tại.
user1046037 13/08/17

1
Tôi đã có chính xác cùng một mã làm việc cho tôi trong một thời gian nhưng bị hỏng trong bản phát hành Xcode sau này. Điều này là khá khó chịu. Có rất nhiều phương thức tùy chọn trong các giao thức Objective-C.
Khởi hành từ

0

Tôi chỉ gặp phải vấn đề này sau khi bật 'tính ổn định của mô-đun' (bật 'Xây dựng thư viện để phân phối') trong một khuôn khổ nhanh chóng mà tôi sử dụng.

Những gì tôi có là một cái gì đó như thế này:

class AwesomeClass: LessAwesomeClass {
...
}

extension AwesomeClass: GreatDelegate {
  func niceDelegateFunc() {
  }
}

Hàm trong tiện ích mở rộng có những lỗi sau:

  • Phương thức phiên bản '@objc' trong phần mở rộng của lớp con 'LessAwesomeClass' yêu cầu iOS 13.0.0

  • Phương thức non - '@ objc' 'niceDelegateFunc' không đáp ứng yêu cầu của giao thức '@objc' 'GreatDelegate'

Di chuyển các chức năng vào lớp thay vì trong một phần mở rộng đã giải quyết được vấn đề.


0

Đây là một giải pháp khác. Tôi cũng gặp phải vấn đề này và chưa thể chuyển từ UIKit sang SwiftUI. Di chuyển các triển khai mặc định vào một lớp cơ sở chung cũng không phải là một lựa chọn đối với tôi. Các triển khai mặc định của tôi khá rộng rãi nên tôi thực sự không muốn có tất cả mã đó bị trùng lặp. Cách giải quyết mà tôi đã kết thúc là sử dụng các hàm wrapper trong giao thức, và sau đó chỉ cần gọi các hàm đó từ mỗi lớp. Không đẹp, nhưng có thể tốt hơn lựa chọn thay thế, tùy thuộc vào tình hình. Mã của bạn sau đó sẽ trông giống như sau:

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {
}

extension P1 where Self : UIViewController {
    func wrapPresentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return UIViewController()
    }
}

class A : UIViewController, P1 {
    func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
        return wrapPresentationController(controller, viewControllerForAdaptivePresentationStyle: style)
    }
}
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.