Ошибка компилятора: метод с селектором Objective C конфликтует с предыдущим объявлением с тем же селектором Objective C

185

Я начинаю изучать Свифт и слежу за очень хорошими лекциями в Стэнфордском университете на 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 selector "выполнить:" конфликтует с предыдущим объявлением с тем же селектором Objective-C

Просто удалив подкласс UIViewController, компиляция кода:

import UIKit

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

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

Некоторая другая информация, которая может быть или не быть релевантной:

  • Недавно я обновился до Йосемити.
  • Когда я установил Xcode, я закончил с бета-версией (версия 6.3 (6D543q)), потому что (если я правильно помню) это была версия, которую мне нужно было запустить в моей версии OS X.

Я наполовину надеюсь, что это ошибка в компиляторе, потому что в противном случае это не имеет никакого смысла для меня. Любая помощь очень благодарна!

  • 3
    Вы можете запустить Xcode 6.2 на Yosemite. Вы можете скачать его из магазина приложений, и он может жить в вашей системе с бета-версией. Я бы не рекомендовал использовать Xcode 6.3 для класса Stanford на данный момент, потому что он бета и включает в себя Swift 1.2, который отличается от предыдущей версии Swift, используемой в видео.
  • 2
    (В настоящее время принятый) ответ пользователя (февраль) от 5 апреля больше не является лучшим. Вместо этого ответ (Джеймс Чжан) от 16 апреля является более конкретным и правильным.
Теги:

6 ответов

129
Лучший ответ

Objective-C не поддерживает перегрузку метода, вы должны использовать другое имя метода. Когда вы унаследовали UIViewController, вы унаследовали NSObject и сделали класс совместимым с Obj-C. С другой стороны, Swift поддерживает перегрузку, поэтому она работает, когда вы удаляете наследование.

  • 3
    Вот правильный ответ: stackoverflow.com/a/29670644/309046
  • 2
    Переопределение метода поддержки Objective-C (с константой (подавляемых) предупреждений компилятора, уведомляющих вас о перегрузке чего-либо уже реализованного), Apple просто не хочет, чтобы вы делали так, чтобы их структуры не перегружались. Я использую такие перегрузки, например, на UIFont каждый день.
Показать ещё 1 комментарий
236

Я тоже принимаю курс Standford, и я тоже застрял здесь, но после некоторых поисков я нашел что-то отсюда: Примечания к выпуску 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)

Переопределения методов, открытых до Objective-C в частных подклассах не предполагается, что это @objc, что приводит к сбою компилятора Swift. Явно добавьте @objc атрибут для любых таких методов переопределения. (19935352)

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

то, что я сделал, просто добавляло "private" перед методом переопределения вроде этого:

    private func performOperation(operation: Double -> Double) {
    if operandStack.count >= 1 {
        displayValue = operation(operandStack.removeLast())
        enter()
    }
}
  • 3
    Это решение является наиболее жизнеспособным, как мне кажется, imho, так как имеет смысл установить этот метод закрытым.
  • 35
    Обратите внимание, что теперь есть также атрибут @nonobjc, который можно использовать для исключения метода из среды выполнения Objective-C.
Показать ещё 2 комментария
105

Как уже было сказано, 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 (и выше). должен быть обновлен как наиболее правильный ответ. ти.
  • 0
    Что насчет xcode 6.4?
Показать ещё 1 комментарий
14

Задача 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, чтобы понять стиль наименования методов или прочитать Имена методов должны быть выразительными и уникальными в пределах класса

  • 0
    Спасибо - это отлично отвечает на мой вопрос, и, поскольку вы были первым, я отмечу это как правильное.
  • 0
    Сказав, что я до сих пор не понимаю, почему код лекции работал, я уверен, что он сделал то, что сделал мой некомпилируемый код! Эй, хо - я вернусь и проверю это дважды. Там должно быть что-то другое.
Показать ещё 3 комментария
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 {

Ещё вопросы

Сообщество Overcoder
Наверх
Меню