Bagaimana saya bisa menangani penghentian inferensi @objc dengan #selector () di Swift 4?

131

Saya mencoba mengubah kode sumber proyek saya dari Swift 3 ke Swift 4. Satu peringatan yang diberikan Xcode kepada saya adalah tentang penyeleksi saya.

Misalnya, saya menambahkan target ke tombol menggunakan pemilih biasa seperti ini:

button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)

Ini peringatan yang ditunjukkannya:

Argumen '#selector' merujuk ke metode instance 'myAction ()' di 'ViewController' yang bergantung pada inferensi atribut '@objc' yang tidak digunakan lagi dalam Swift 4

Tambahkan '@objc' untuk mengekspos metode instance ini ke Objective-C

Sekarang, menekan Fixpada pesan kesalahan melakukan ini untuk fungsi saya:

// before
func myAction() { /* ... */ }

// after
@objc func myAction() { /* ... */ }

Saya tidak benar-benar ingin mengubah nama semua fungsi saya untuk memasukkan @objctanda dan saya berasumsi itu tidak perlu.

Bagaimana cara menulis ulang pemilih untuk mengatasi penghinaan?


Pertanyaan terkait:

LinusGeffarth
sumber
3
Tidak, menandai mereka sebagai @objc yang sekarang diperlukan dalam rangka untuk mengekspos mereka untuk obj-C, dan karena itu digunakan dengan pemilih.
Hamish
3
Jadi bagian yang sudah usang menyimpulkan fungsi akses publik sebagai @objc? Itu agak menjengkelkan, tapi saya biasanya membuat fungsi-fungsi ini pribadi, mengharuskan saya untuk menandainya sebagai @objcanyways.
Connor
2
Lihat SE-0160 untuk info lebih lanjut tentang perubahan tersebut. Alternatif lain adalah menandai kelas yang diberikan @objcMembersuntuk mengekspos semua anggota yang kompatibel dengan Obj-C ke Obj-C, tetapi saya tidak akan menyarankan itu kecuali Anda benar-benar membutuhkan seluruh kelas Anda untuk diekspos.
Hamish
3
@LinusGeffarth Seperti yang dikatakan dalam proposal, kemungkinan akan meningkatkan ukuran biner Anda dan menghubungkan dinamis tidak perlu lebih lama. Saya benar-benar tidak berpikir itu terlalu merepotkan untuk kejelasan tambahan yang Anda maksudkan secara khusus untuk hal tertentu untuk digunakan dari Obj-C.
Hamish
1
Sudah mencoba, tidak bekerja.
LinusGeffarth

Jawaban:

156

Perbaiki-itu benar - tidak ada tentang pemilih yang dapat Anda ubah untuk membuat metode yang merujuk ke terkena Objective-C.

Seluruh alasan untuk peringatan ini sejak awal adalah hasil dari SE-0160 . Sebelum Swift 4, internalatau lebih tinggi anggota yang kompatibel dengan Objective-C dariNSObject kelas inherit disimpulkan @objcdan karenanya terkena Objective-C, oleh karena itu memungkinkan mereka untuk dipanggil menggunakan penyeleksi (karena runtime Obj-C diperlukan untuk mencari metode ini implementasi untuk pemilih tertentu).

Namun dalam Swift 4, ini tidak lagi terjadi. Hanya deklarasi yang sangat spesifik sekarang disimpulkan menjadi @objc, misalnya, menimpa @objcmetode, implementasi @objcpersyaratan protokol dan deklarasi dengan atribut yang menyiratkan @objc, seperti @IBOutlet.

Motivasi di balik ini, sebagaimana dirinci dalam proposal terkait di atas , adalah pertama-tama untuk mencegah kelebihan metode dalam NSObjectmewarisi kelas yang saling bertabrakan karena memiliki penyeleksi yang identik. Kedua, ini membantu mengurangi ukuran biner dengan tidak harus menghasilkan thunks untuk anggota yang tidak perlu terkena Obj-C, dan ketiga meningkatkan kecepatan penghubung dinamis.

Jika Anda ingin mengekspos anggota ke Obj-C, Anda harus menandainya sebagai @objc, misalnya:

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        button.addTarget(self, action: #selector(foo), for: .touchUpInside)
    }

    @objc func foo() {
       // ... 
    }
}

(migrator harus melakukan ini secara otomatis untuk Anda dengan penyeleksi ketika menjalankan dengan opsi "meminimalkan inferensi" yang dipilih)

Untuk memaparkan sekelompok anggota ke Obj-C, Anda dapat menggunakan @objc extension:

@objc extension ViewController {

    // both exposed to Obj-C
    func foo() {}
    func bar() {}
}

Ini akan mengekspos semua anggota yang didefinisikan di dalamnya untuk Obj-C, dan memberikan kesalahan pada setiap anggota yang tidak dapat terkena Obj-C (kecuali secara eksplisit ditandai sebagai @nonobjc ).

Jika Anda memiliki kelas di mana Anda membutuhkan semua anggota yang kompatibel dengan Obj-C untuk terkena Obj-C, Anda dapat menandai kelas sebagai @objcMembers:

@objcMembers
class ViewController: UIViewController {
   // ...
}

Sekarang, semua anggota yang dapat disimpulkan @objcakan menjadi. Namun, saya tidak akan menyarankan melakukan ini kecuali Anda benar - benar perlu semua anggota terkena Obj-C, mengingat kelemahan yang disebutkan di atas memiliki anggota yang tidak perlu terpapar.

Hamish
sumber
2
Mengapa Xcode tidak secara otomatis melakukan itu ketika mengubah kode ke sintaks terbaru?
LinusGeffarth
1
Saya bertanya-tanya, apakah @IBActionjuga secara otomatis @objc? itu tidak terjadi sampai sekarang tetapi itu akan logis. EDIT: nvm, proposal dengan jelas menyatakan bahwa @IBActioncukup untuk suatu metode @objc.
Sulthan
8
Saya tidak mengerti semua ini. Saya tidak punya kode Objective-C apa pun. Apakah tidak ada cara Swift murni untuk menambahkan target ke tombol? Atau gunakan penyeleksi? Saya tidak ingin kode saya penuh dengan atribut ini. Apakah karena UIButton berasal dari beberapa NSObject / Obj-c-thing?
Ims
11
@Sti Jadi untuk langsung menjawab " Apakah tidak ada Swift-cara murni menambahkan target ke tombol? Atau gunakan penyeleksi? " - tidak, tidak ada cara "murni Swift" menggunakan penyeleksi untuk mengirimkan metode. Mereka bergantung pada runtime Obj-C untuk mencari implementasi metode untuk memanggil pemilih tertentu - runtime Swift tidak memiliki kemampuan itu.
Hamish
12
Penyeleksi pria berantakan. Sepertinya setiap pembaruan cepat yang mereka mainkan dengannya. Mengapa kita tidak bisa hanya memiliki pemilih autocomplete swift murni untuk metode. Membuat kode terlihat sangat jelek.
John Riselvato
16

Sebagai Dokumentasi Resmi Apple . Anda perlu menggunakan @objc untuk memanggil Metode Pemilihan Anda.

Dalam Objective-C, selector adalah tipe yang merujuk pada nama metode Objective-C. Dalam Swift, penyeleksi Objective-C diwakili oleh Selectorstruktur, dan dapat dibangun menggunakan #selector ekspresi. Untuk membuat pemilih untuk metode yang dapat dipanggil dari Objective-C, berikan nama metode, seperti #selector(MyViewController.tappedButton(sender:)). Untuk membangun pemilih untuk metode Objective-C pengambil atau penyetel properti, berikan nama properti yang diawali oleh label getter:atau setter:, seperti #selector(getter: MyViewController.myButton).

Kiran Sarvaiya
sumber
Yang saya mengerti dari ini adalah bahwa dalam beberapa kasus saya perlu menambahkan @objc ke awal sebuah func apakah saya akan memanggilnya dari Objective-C atau tidak. Saya baru belajar Swift menggunakan Obj-C selama bertahun
SundialSoft
10

Pada, saya pikir Swift 4.2, yang perlu Anda lakukan hanyalah menetapkan @IBAction ke metode Anda dan Anda dapat menghindari anotasi @objc konyol ini

`` `

let tap  =  UITapGestureRecognizer(target: self, action: #selector(self.cancel))


@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}
Logan Sease
sumber
1
Ini juga akan memberi tahu pembuat antarmuka bahwa fungsi ini dapat dihubungkan, yang mungkin bukan yang Anda inginkan. Itu juga melakukan hal yang sama dengan @objc.
Josh Paradroid
2

Seperti yang telah disebutkan dalam jawaban lain, tidak ada cara untuk menghindari @objcanotasi bagi pemilih.

Tetapi peringatan yang disebutkan dalam OP dapat dibungkam dengan mengambil langkah-langkah berikut:

  1. Pergi ke Pengaturan Bangun
  2. Cari kata kunci @objc
  3. Tetapkan nilai antarmuka Swift 3 @objc keOff

di bawah ini adalah tangkapan layar yang menggambarkan langkah-langkah yang disebutkan di atas:

Membungkam peringatan "Antarmuka Swift 3 @objc"

Semoga ini membantu

WARRIOR SAHAM
sumber
Bagaimana ini mempengaruhi proyek Anda dalam gambaran yang lebih besar?
Zonily Jame
1
Bagaimana dengan * .ipa atau * .xarchive size, dan build time?
Zonily Jame
@ZonilyJame saya tidak melihat waktu pembuatan, tetapi tidak ada efek pada ukuran IPA
S1LENT WARRIOR
Ini bukan nilai yang disarankan yang dapat Anda gunakan jika Anda menggunakan versi swift 4.0+ sesuai dengan pedoman Apple. Periksa bantuan ini.apple.com/xcode/mac/current/#/deve838b19a1
Bhavin_m
1
Anda dapat memperbarui jawaban ini dengan menetapkan nilai ke Default dalam Xcode 11. Sangat penting untuk menetapkan nilai pada proyek dan target Anda untuk menghapus peringatan sepenuhnya.
Tommie C.
2

Jika Anda memerlukan anggota c yang obyektif di pengontrol tampilan Anda, tambahkan saja @objcMembers di bagian atas pengontrol tampilan. Dan Anda dapat menghindari ini dengan menambahkan IBAction dalam kode Anda.

@IBAction func buttonAction() {

}

Pastikan untuk menghubungkan outlet ini di storyboard.

Renjish C
sumber