Kesalahan kompiler: Metode dengan konflik Objective-C konflik dengan pernyataan sebelumnya dengan pemilih Objective-C yang sama

209

Saya mulai belajar Swift, dan telah mengikuti ceramah video Stanford University yang sangat bagus di YouTube. Berikut ini tautan jika Anda tertarik atau itu membantu (walaupun tidak diperlukan untuk memahami masalah saya):

Mengembangkan Aplikasi iOS 8 dengan Swift - 2. Lebih banyak Xcode dan Swift, MVC

Sambil mengikuti kuliah saya sampai pada titik di mana (sejauh yang saya tahu) kode saya identik dengan kode dalam video tetapi pada sistem saya, saya mendapat kesalahan kompiler. Setelah banyak percobaan dan kesalahan saya berhasil mengurangi kode saya menjadi dua contoh, satu di antaranya menghasilkan kesalahan, yang lain atau yang tidak, tetapi saya tidak tahu apa yang sebenarnya menyebabkan kesalahan atau bagaimana menyelesaikannya.

Kode yang menyebabkan kesalahan adalah:

import UIKit

class BugViewController: UIViewController
{
    func perform(operation: (Double) -> Double) {
    }

    func perform(operation: (Double, Double) -> Double) {
    }
}

Ini menciptakan kesalahan kompilator berikut:

Metode 'perform' dengan pemilih Objective-C konflik 'perform:' dengan deklarasi sebelumnya dengan pemilih Objective-C yang sama

Dengan hanya menghapus sub-classing dari UIViewController kompilasi kode:

import UIKit

class BugViewController
{
    func perform(operation: (Double) -> Double) {
    }

    func perform(operation: (Double, Double) -> Double) {
    }
}

Beberapa informasi lain yang mungkin relevan atau tidak relevan:

  • Saya baru saja ditingkatkan ke Yosemite.
  • Ketika saya menginstal Xcode, saya berakhir dengan versi Beta (Versi 6.3 (6D543q)) karena (jika saya ingat dengan benar) ini adalah versi yang saya perlukan untuk berjalan di versi OS X.

Saya setengah berharap ini adalah bug di kompiler karena kalau tidak, ini tidak masuk akal bagi saya. Bantuan apa pun dengan penuh terima kasih diterima!

Pertanda baik
sumber
3
Anda dapat menjalankan Xcode 6.2 di Yosemite. Anda dapat mengunduhnya dari app store dan dapat hidup di sistem Anda dengan versi Beta. Saya tidak akan merekomendasikan menggunakan Xcode 6.3 untuk kelas Stanford pada saat ini karena ini beta dan termasuk Swift 1.2 yang berbeda dari versi Swift sebelumnya yang digunakan dalam video.
vacawama
2
Jawaban (saat ini diterima) dari pengguna (feb) mulai 5 April bukan yang terbaik lagi. Sebaliknya jawaban dari (James Zhang) dari 16 April lebih spesifik dan benar.
phlebotinum

Jawaban:

144

Objective-C tidak mendukung kelebihan metode, Anda harus menggunakan nama metode yang berbeda. Saat Anda mewarisi UIViewController, Anda mewarisi NSObject dan membuat kelas bisa di-interopable ke Obj-C. Swift di sisi lain mendukung kelebihan beban, itulah mengapa ia bekerja saat Anda menghapus warisan.

feb
sumber
2
Metode Objective-C SUPPORTS meng-override (dengan peringatan compiler (tertekan) yang memberitahukan Anda tentang overload sesuatu yang sudah diimplementasikan), Apple tidak ingin Anda melakukannya untuk menjaga kerangka kerja mereka dari kelebihan beban. Saya menggunakan kelebihan seperti itu di UIFontsetiap hari.
Michi
@ polarwar jawaban di bawah ini adalah yang terbaik untuk Swift 2: stackoverflow.com/a/31500740/144088
Crashalot
237

Saya sendiri juga mengambil kursus Standford dan saya terjebak di sini untuk waktu yang lama juga, tetapi setelah beberapa pencarian, saya menemukan sesuatu dari sini: Catatan rilis Xcode dan menyebutkan sesuatu di bawah:

Swift 1.2 sangat ketat dalam memeriksa overloading berbasis-metode @objc dan inisialisasi, sesuatu yang tidak didukung oleh Objective-C.

// Has the Objective-C selector "performOperation:".
func performOperation(op: NSOperation) { /* do something */ }
// Also has the selector "performOperation:".
func performOperation(fn: () -> Void) {
    self.performOperation(NSBlockOperation(block: fn))
}

Kode ini akan berfungsi ketika dipanggil dari Swift, tetapi bisa dengan mudah mogok jika dipanggil dari Objective-C. Untuk mengatasi masalah ini, gunakan tipe yang tidak didukung oleh Objective-C untuk mencegah kompiler Swift dari mengekspos anggota ke runtime Objective-C:

  • Jika masuk akal, tandai anggota sebagai pribadi untuk menonaktifkan inferensi @objc.
  • Jika tidak, gunakan parameter dummy dengan nilai default, misalnya: _ nonobjc: () = (). (19826275)

Override metode yang diekspos ke Objective-C di subclass pribadi tidak disimpulkan sebagai @objc, menyebabkan compiler Swift mogok. Secara eksplisit menambahkan atribut @objc ke metode pengesampingan tersebut. (19935352)

Simbol dari SDK tidak tersedia saat menggunakan Buka Cepat di proyek atau ruang kerja yang menggunakan Swift. (20349540)

yang saya lakukan hanya menambahkan "pribadi" di depan metode override seperti ini:

    private func performOperation(operation: Double -> Double) {
    if operandStack.count >= 1 {
        displayValue = operation(operandStack.removeLast())
        enter()
    }
}
James Zhang
sumber
3
Solusi ini adalah yang paling layak saya temukan imho, karena ini benar-benar masuk akal untuk menetapkan metode ini pribadi
demental
38
Harap perhatikan bahwa sekarang ada juga atribut @nonobjc, yang dapat digunakan untuk mengecualikan metode dari runtime Objective-C.
Erik J
2
Saya kedua komentar @ ErikJ dan jawaban polarwar di bawah ini. Ini sepertinya jawaban terbaik untuk bergerak maju dengan Swift 2 dan xcode 7. Jika Anda belum memperbarui, saya sangat merekomendasikannya.
Austin A
@ polarwar jawaban di bawah ini adalah yang terbaik untuk Swift 2: stackoverflow.com/a/31500740/144088
Crashalot
111

Seperti yang sudah dijawab, ObjC tidak mendukung metode overloading (dua metode dengan nama yang sama) dan In swift 2 di bawah Xcode 7 ada dua opsi untuk menyelesaikan masalah seperti ini. Salah satu opsi adalah mengubah nama metode menggunakan atribut:@objc(newNameMethod:)

func methodOne(par1, par2) {...}

@objc(methodTwo:)
func methodOne(par1) {...}

opsi lain untuk mengatasi masalah ini di Xcode 7+ adalah dengan menerapkan @nonobjcatribut ke metode, subskrip atau penginisialisasi apa pun

func methodOne() {...}

@nonobjc
func methodOne() {...}
polarware
sumber
6
ini menyelesaikan masalah untuk swift 2 (dan lebih tinggi). harus diperbarui sebagai jawaban yang paling benar. ty.
Maxim Veksler
2
Bagi siapa pun yang menggunakan Swift 2 dan Xcode 7 + ini adalah jawaban yang benar saya setuju dengan polarwar
TerNovi
17

Masalah adalah UIViewControlleradalah @objckelas. Ketika mewarisi dari UIViewController, BugViewControllerjuga a@objc kelas.

Ini berarti harus sesuai dengan aturan penyeleksi Objective-C (nama metode). Metode func perform(operation: (Double) -> Double)dan func perform(operation: (Double, Double) -> Double)keduanya memiliki pemilih yang sama@selector(perform:) . Ini tidak diizinkan

Untuk mengatasi ini, gunakan nama yang berbeda: suka func perform1(operation: (Double) -> Double)dan func perform2(operation: (Double, Double) -> Double).


Saya pikir cara terbaik untuk menangani ini adalah memberikan perform()metode Anda nama yang lebih deskriptif. Apa yang dilakukan metode ini? Bagaimana mereka mengubah status pengontrol tampilan? Lihatlah UIViewControllermetode lain untuk merasakan gaya penamaan metode, atau baca Metode Nama Harus Ekspresif dan Unik di Dalam Kelas

Jeffery Thomas
sumber
Terima kasih - ini menjawab pertanyaan saya dengan sempurna dan karena Anda yang pertama, saya akan menandainya dengan benar.
Auspice
Setelah mengatakan bahwa saya masih tidak mengerti mengapa kode pada ceramah bekerja karena saya cukup yakin itu melakukan apa yang kode non-kompilasi saya lakukan! Hei ho - aku akan kembali dan mengeceknya. Pasti ada sesuatu yang berbeda.
Auspice
2
@Auspice Mungkin tidak menghasilkan kesalahan dengan versi Xcode yang mereka gunakan untuk video tetapi masih menjadi masalah. Baru pada Xcode 6.3 kompiler dapat mendeteksi ini dan memperingatkan Anda.
Mick MacCallum
3
Paul Hegarty ingin mendemonstrasikan fungsi 'kelebihan beban' di sini (2 fungsi dengan nama yang sama, tetapi serangkaian argumen berbeda), jadi dia sengaja menggunakan nama metode yang sama! Kelebihan muatan hanya diperbolehkan di Swift, bukan di Objective-C. Itu sebabnya solusinya adalah dengan menghapus bentuk warisan UIViewController (yang merupakan kelas Objective-C), atau mendeklarasikan metode pribadi. Kedua solusi dijelaskan secara terperinci dalam jawaban lain di sini.
Ronny Webers
Sebenarnya saya menggunakan kata kunci pribadi di depan fungsi. seperti, fungsi priv performOperation (operasi: Double -> Double) {} dan fungsi priv performOperation (operasi: (Double, Double) -> Double) {} Di sini saya mencapai metode overloading dengan bantuan PRIVATE. karena saya menggunakan keduanya di ViewController.Swift saja. Mengapa kompiler tidak mengatakan Kesalahan?
ITag
2

Saya mendapat kesalahan yang sama karena memiliki dua metode dengan tanda tangan Obj-C yang sama:

static func prepareForUpSyncing(obj : NSManagedObject!) -> Bool
static func prepareForUpSyncing(objs : [NSManagedObject]!) -> Bool

Saya tidak ingin menandai salah satu dari mereka sebagai @nonobjc karena kemungkinan konsekuensi yang tidak terduga pada saat runtime. (Seseorang dapat memperbaiki saya jika tidak ada kemungkinan)

Diatasi dengan menggunakan fitur nama parameter eksternal Swift (saya membuat nama eksternal sama dengan nama lokal) ke metode kedua, yang secara efektif mengubah tanda tangan metode Obj-c:

static func prepareForUpSyncing(objs objs : [NSManagedObject]!) -> Bool {
Protongun
sumber