Помилка компілятора: метод із селектором Objective-C суперечить попередньому оголошенню тим самим селектором Objective-C


209

Я починаю вивчати Свіфт, і слідкую за дуже хорошими відео лекціями університету Стенфорда на YouTube. Ось посилання, якщо вам це цікаво, або це допомагає (хоча для розуміння моєї проблеми не потрібно):

Розробка програм для iOS 8 за допомогою Swift - 2. Більше Xcode та Swift, MVC

Під час виконання лекцій я дійшов до моменту, коли (наскільки я міг сказати) мій код був ідентичним коду на відео, але в моїй системі я отримав помилку компілятора. Після безлічі спроб і помилок мені вдалося зменшити свій код до двох прикладів, один з яких генерує помилку, другий чи ні, але я не маю поняття, що насправді викликає помилку або як її усунути.

Код, який створює помилку:

import UIKit

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

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

Це створює таку помилку компілятора:

Метод "виконувати" із селектором Objective-C "виконувати:" суперечить попередньому оголошенню тим самим селектором Objective-C

Просто видаливши підкласифікацію UIViewController, код складається:

import UIKit

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

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

Деякі інші відомості, які можуть бути або не бути актуальними:

  • Я нещодавно перейшов до Йосеміті.
  • Коли я встановив Xcode, я отримав бета-версію (версія 6.3 (6D543q)), оскільки (якщо я правильно пам'ятаю) це була версія, яку мені потрібно було запустити на моїй версії ОС X.

Я наполовину сподіваюся, що це помилка в компіляторі, оскільки в іншому випадку це не має для мене сенсу. Будь-яка допомога дуже вдячно отримана!


3
Ви можете запустити Xcode 6.2 на Yosemite. Ви можете завантажити його з магазину додатків, і він може жити у вашій системі з версією Beta. Я б не рекомендував використовувати Xcode 6.3 для класу Стенфорд в даний момент, оскільки він бета-версія і включає в себе Swift 1.2, який відрізняється від попередньої версії Swift, що використовується у відео.
vacawama

2
Відповідь користувача (феб) від 5 квітня вже не найкраща. Натомість відповідь (Джеймс Чжан) від 16 квітня більш конкретна та правильна.
флеботінум

Відповіді:


144

Objective-C не підтримує перевантаження методу, ви повинні використовувати іншу назву методу. Коли ви успадкували UIViewController, ви успадкували NSObject і зробили клас інтеропатобельним до Obj-C. Swift з іншого боку підтримує перевантаження, тому воно працює, коли ви видаляєте спадщину.


2
Об'єктивний метод ПІДТРИМКИ, що переосмислює (з урахуванням попереджувальних попереджень компілятора, що сповіщають вас про перевантаження вже реалізованого), Apple просто не хоче, щоб ви це робили, щоб утримати їх рамки від перевантаження. Я використовую такі перевантаження fe UIFontщодня.
Мічі

@ Відповідь polarwar в нижче є найкращим варіантом для Swift 2: stackoverflow.com/a/31500740/144088
Crashalot

237

Я сам також беру курс по Стендфорду, і я тут теж давно застряг, але після деяких пошуків я знайшов щось тут: примітки до випуску Xcode, і це щось згадувало нижче:

Swift 1.2 суворо стосується перевірки на основі типу перевантаження методів @objc та ініціалізаторів, що не підтримується 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))
}

Цей код буде працювати при виклику від Swift, але може легко збій, якщо його викликати від Objective-C. Щоб вирішити цю проблему, використовуйте тип, який не підтримується Objective-C, щоб запобігти компілятору Swift не піддавати учасника виконанню Objective-C:

  • Якщо це має сенс, позначте члена як приватного, щоб відключити висновок @objc.
  • В іншому випадку використовуйте фіктивний параметр зі значенням за замовчуванням, наприклад: _ nonobjc: () = (). (19826275)

Заміни методів, які піддаються об’єкту-C в приватних підкласах, не вважаються @objc, що призводить до збоїв компілятора Swift. Явно додайте атрибут @objc до будь-яких таких переважаючих методів. (19935352)

Символи з SDK недоступні при використанні Open Quick в проекті або робочій області, де використовується Swift. (20349540)

що я зробив, просто додав "приватне" перед методом переосмислення, як це:

    private func performOperation(operation: Double -> Double) {
    if operandStack.count >= 1 {
        displayValue = operation(operandStack.removeLast())
        enter()
    }
}

3
Це рішення є найбільш життєздатним, я вважаю імхо, оскільки це абсолютно має сенс встановлювати цей метод приватним
дементологічний

38
Зауважте, що зараз є також атрибут @nonobjc, який можна використовувати для виключення методу з часу виконання Objective-C.
Ерік Дж

2
Я другий коментар @ Ерік Дж і відповідь полярної війни нижче. Здається, найкраща відповідь рухається вперед із Swift 2 та xcode 7. Якщо ви ще не оновлювались, настійно рекомендую.
Остін А

@ Відповідь polarwar в нижче є найкращим варіантом для Swift 2: stackoverflow.com/a/31500740/144088
Crashalot

111

Як уже було отримано відповіді, ObjC не підтримує метод перевантаження (два методи з однаковою назвою), а в Swift 2 під Xcode 7 є два варіанти вирішення подібних проблем. Один варіант - перейменувати метод за допомогою атрибута:@objc(newNameMethod:)

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

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

Ще один варіант вирішення цієї проблеми в Xcode 7+ - це застосування @nonobjcатрибутів до будь-якого методу, підписника або ініціалізатора

func methodOne() {...}

@nonobjc
func methodOne() {...}

6
це вирішує проблему для швидких 2 (і вище). слід оновити як найбільш правильну відповідь. ти.
Максим Векслер

2
Для тих, хто використовує Swift 2 та Xcode 7 + це правильна відповідь, я згоден з polarwar
TerNovi

17

Проблема в тому , UIViewControllerє @objcклас. При спадкуванні від UIViewController, BugViewControllerтакож є @objcклас.

Це означає, що він повинен відповідати правилам селекторів Objective-C (назва методу). Методи func perform(operation: (Double) -> Double)і func perform(operation: (Double, Double) -> Double)обидва мають один і той же селектор @selector(perform:). Це заборонено.

Щоб вирішити це, використовуйте різні назви: як func perform1(operation: (Double) -> Double)і func perform2(operation: (Double, Double) -> Double).


Я думаю, що найкращий спосіб вирішити це - дати своїм perform()методам більш описові назви. Що роблять ці методи? Як вони змінюють стан контролера перегляду? Подивіться на інші UIViewControllerметоди, щоб відчути стиль назви методів, або прочитайте назви методів, які повинні бути виразними та унікальними в межах класу


Дякую - це прекрасно відповідає на моє запитання, і, як ви були першими, я відзначу це як правильне.
Auspice

Сказавши, що я досі не розумію, чому код на лекції працював, оскільки я впевнений, що він зробив те, що зробив мій некомпілюючий код! Гей хо - я повернусь і ще раз перевірю. Має бути щось інше.
Auspice

2
@Auspice Можливо, це не призвело до помилок у версії Xcode, яку вони використовували для відео, але це все-таки була проблемою. Лише Xcode 6.3, що компілятор не зміг виявити це, попереджає вас.
Мік Маккаллум

3
Пол Хегарті хоче продемонструвати тут функцію "перевантаження" (2 функції з тим же ім'ям, але різним набором аргументів), тому він спеціально використовує те саме ім'я методу! Перевантаження дозволено лише у Swift, а не в Objective-C. Ось чому рішенням є або видалити спадкову форму UIViewController (яка є класом Objective-C), або оголосити метод приватним. Обидва рішення детально пояснені в інших відповідях тут.
Ронні Веберс

Насправді я використовував приватне ключове слово перед функцією. наприклад, приватні func performOperation (операція: Double -> Double) {} та private func performOperation (операція: (Double, Double) -> Double) {} Тут я досяг методу перевантаження за допомогою PRIVATE. тому що я використовував їх обох у ViewController.Swift. Чому компілятор не каже помилки?
iTag

5

З https://developer.apple.com/library/ios/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc6_release_notes.html у розділі "Примітки до випуску Xcode 6.3" -> "Швидкі зміни мови", які ви знайдете

Тепер Swift виявляє розбіжності між перевантаженням і переосмисленням системи типу Swift та ефективної поведінки, що спостерігається через час Objective-C.


2

Я отримав ту ж помилку через те, що мав два методи з однаковим підписом Obj-C:

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

Я не хотів позначати одного з них як @nonobjc через можливість непередбачуваних наслідків під час виконання. (Хтось може виправити мене, якщо немає можливості)

Вирішили це за допомогою функції зовнішнього параметра Swift (я зробив зовнішнє ім'я таким же, як локальне ім'я) до другого методу, який ефективно змінює підпис методу Obj-c:

static func prepareForUpSyncing(objs objs : [NSManagedObject]!) -> Bool {
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.