Metode non - '@ objc' tidak memenuhi persyaratan opsional protokol '@objc'

104

Gambaran:

  • Saya memiliki protokol P1 yang menyediakan implementasi default dari salah satu fungsi opsional Objective-C.
  • Ketika saya menyediakan implementasi default dari fungsi opsional, ada peringatan

Peringatan Penyusun:

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

Versi: kapan:

  • Cepat: 3
  • Xcode: 8 (rilis publik)

Upaya yang dilakukan:

  • Mencoba menambahkan @objctetapi tidak membantu

Pertanyaan:

  • Bagaimana cara mengatasi ini?
  • Apakah ada solusi?

Kode:

@objc protocol P1 : UIAdaptivePresentationControllerDelegate {

}

extension P1 where Self : UIViewController {

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


class A : UIViewController, P1 {

}
pengguna1046037
sumber
Apakah Anda memiliki Xcode versi terbaru? Saya tidak mendapatkan kesalahan apa pun jika saya menghapus@objc
Qbyte
Saya menggunakan Xcode 8 (versi publik terbaru). Tidak ada kesalahan, tetapi akan ada peringatan
pengguna1046037

Jawaban:

183

Meskipun saya pikir saya bisa menjawab pertanyaan Anda, itu bukan jawaban yang Anda sukai.

TL; DR: @objc fungsi saat ini mungkin tidak ada dalam ekstensi protokol. Anda dapat membuat kelas dasar sebagai gantinya, meskipun itu bukan solusi yang ideal.

Ekstensi Protokol dan Objective-C

Pertama, pertanyaan / jawaban ini ( Can Swift Method Defined on Extensions on Protocols Accessed in Objective-c ) tampaknya menyarankan bahwa karena cara ekstensi protokol dikirim di bawah tenda, metode yang dideklarasikan dalam ekstensi protokol tidak terlihat oleh objc_msgSend()fungsi tersebut, dan oleh karena itu tidak terlihat oleh kode Objective-C. Karena metode yang Anda coba tentukan dalam ekstensi Anda harus terlihat oleh Objective-C (sehingga Anda UIKitdapat menggunakannya), ia berteriak pada Anda untuk tidak menyertakannya @objc, tetapi begitu Anda memasukkannya, ia akan berteriak kepada Anda karena@objc tidak diizinkan di ekstensi protokol. Ini mungkin karena ekstensi protokol saat ini tidak dapat dilihat oleh Objective-C.

Kita juga dapat melihat bahwa pesan kesalahan setelah kita menambahkan @objcstatus "@objc hanya dapat digunakan dengan anggota kelas, protokol @objc, dan ekstensi konkret kelas." Ini bukan kelas; ekstensi ke protokol @objc tidak sama dengan definisi protokol itu sendiri (yaitu dalam persyaratan), dan kata "konkret" akan menyarankan bahwa ekstensi protokol tidak dihitung sebagai ekstensi kelas yang konkret.

Solusi

Sayangnya, ini cukup banyak mencegah Anda dari menggunakan ekstensi protokol ketika implementasi default harus terlihat oleh kerangka kerja Objective-C. Pada awalnya, saya pikir mungkin @objctidak diizinkan dalam ekstensi protokol Anda karena Swift Compiler tidak dapat menjamin bahwa tipe yang sesuai akan menjadi kelas (meskipun Anda telah menentukannya secara khusus UIViewController). Jadi saya memasang classpersyaratan P1. Ini tidak berhasil.

Mungkin satu-satunya solusi adalah dengan hanya menggunakan kelas dasar daripada protokol di sini, tetapi ini jelas tidak sepenuhnya ideal karena kelas mungkin hanya memiliki satu kelas dasar tetapi sesuai dengan beberapa protokol.

Jika Anda memilih untuk mengikuti rute ini, harap pertimbangkan pertanyaan ini ( Metode Protokol Opsional ObjC Swift 3 Tidak Disebut dalam Subkelas ) ke dalam akun. Tampaknya masalah lain saat ini di Swift 3 adalah bahwa subclass tidak secara otomatis mewarisi implementasi persyaratan protokol opsional dari superclass mereka. Jawaban atas pertanyaan tersebut menggunakan adaptasi khusus @objcuntuk menyiasatinya.

Melaporkan Masalah

Saya pikir ini sudah dibahas di antara mereka yang mengerjakan proyek sumber terbuka Swift, tetapi Anda dapat yakin mereka mengetahuinya dengan menggunakan Reporter Bug Apple , yang kemungkinan pada akhirnya akan menuju ke Tim Inti Swift, atau reporter bug Swift . Namun, salah satu dari ini mungkin menganggap bug Anda terlalu luas atau sudah diketahui. Tim Swift juga dapat menganggap apa yang Anda cari sebagai fitur bahasa baru, dalam hal ini Anda harus memeriksa milis terlebih dahulu .

Memperbarui

Pada Desember 2016, masalah ini dilaporkan ke komunitas Swift. Masalah tersebut masih ditandai sebagai terbuka dengan prioritas menengah, tetapi komentar berikut telah ditambahkan:

Ini dimaksudkan. Tidak ada cara untuk menambahkan implementasi metode ke setiap pengguna, karena ekstensi dapat ditambahkan setelah kesesuaian dengan protokol. Saya kira kami dapat mengizinkannya jika ekstensi berada dalam modul yang sama dengan protokol.

Karena protokol Anda berada dalam modul yang sama dengan ekstensi Anda, bagaimanapun, Anda mungkin dapat melakukan ini di versi Swift yang akan datang.

Perbarui 2

Pada bulan Februari 2017, masalah ini secara resmi ditutup sebagai "Won't Do" oleh salah satu anggota Swift Core Team dengan pesan berikut:

Ini disengaja: ekstensi protokol tidak dapat memasukkan titik masuk @objc karena keterbatasan runtime Objective-C. Jika Anda ingin menambahkan titik masuk @objc ke NSObject, perpanjang NSObject.

Memperluas NSObjectatau bahkan UIViewControllertidak akan mencapai apa yang Anda inginkan, tetapi sayangnya tampaknya tidak akan menjadi mungkin.

Di masa depan (yang sangat) panjang, kami mungkin dapat menghilangkan ketergantungan pada @objcmetode sepenuhnya, tetapi waktu itu kemungkinan besar tidak akan datang dalam waktu dekat karena kerangka kerja Cocoa saat ini tidak ditulis di Swift (dan tidak bisa sampai memiliki ABI yang stabil) .

Perbarui 3

Mulai Musim Gugur 2019, ini menjadi lebih sedikit masalah karena semakin banyak kerangka kerja Apple yang ditulis di Swift. Misalnya, jika Anda menggunakan SwiftUIalih-alih UIKit, Anda menghindari masalah sepenuhnya karena @objctidak akan pernah diperlukan saat merujuk ke suatu SwiftUImetode.

Kerangka kerja Apple yang ditulis di Swift meliputi:

  • SwiftUI
  • RealityKit
  • Menggabungkan
  • CryptoKit

Orang akan berharap pola ini berlanjut seiring waktu karena Swift secara resmi ABI dan modul stabil pada Swift 5.0 dan 5.1, masing-masing.

Matthew Seaman
sumber
1
@ user1046037 Saya melakukannya juga, karena saya dapat melihat diri saya mengalami masalah ini berkali-kali dalam pengembangan di masa mendatang.
Matthew Seaman
2
Anda benar, jawaban Anda masih berlaku meskipun Swift 4tidak ada alternatif lain seperti sekarang.
pengguna1046037
1
Saya memiliki kode yang persis sama yang berfungsi untuk saya untuk sementara waktu namun melanggar di rilis Xcode selanjutnya. Ini sangat mengesalkan. Ada begitu banyak metode opsional dalam protokol Objective-C.
Departamento B
0

Saya baru saja mengalami ini setelah mengaktifkan 'stabilitas modul' (mengaktifkan 'Membangun perpustakaan untuk distribusi') dalam kerangka kerja yang cepat yang saya gunakan.

Apa yang saya miliki adalah sesuatu seperti ini:

class AwesomeClass: LessAwesomeClass {
...
}

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

Fungsi dalam ekstensi mengalami kesalahan berikut:

  • Metode instance '@objc' dalam ekstensi subclass 'LessAwesomeClass' memerlukan iOS 13.0.0

  • Metode non - '@ objc' 'niceDelegateFunc' tidak memenuhi persyaratan protokol '@objc' 'GreatDelegate'

Memindahkan fungsi ke dalam kelas daripada di ekstensi menyelesaikan masalah.

CMash
sumber
0

Berikut solusi lain. Saya mengalami masalah ini juga, dan belum bisa beralih dari UIKit ke SwiftUI. Memindahkan implementasi default ke kelas dasar umum juga bukan pilihan bagi saya. Implementasi default saya cukup ekstensif jadi saya benar-benar tidak ingin semua kode itu diduplikasi. Solusi yang akhirnya saya gunakan adalah menggunakan fungsi pembungkus dalam protokol, dan kemudian cukup memanggil fungsi tersebut dari setiap kelas. Tidak cantik, tetapi mungkin lebih baik daripada alternatifnya, tergantung situasinya. Kode Anda akan terlihat seperti ini:

@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)
    }
}
rene
sumber