Erro do compilador: o método com o seletor de Objective-C entra em conflito com a declaração anterior com o mesmo seletor de Objective-C

209

Estou começando a aprender Swift e tenho acompanhado as muito boas palestras em vídeo da Universidade de Stanford no YouTube. Aqui está um link, se você estiver interessado ou ajudar (embora não seja necessário entender o meu problema):

Desenvolvendo aplicativos iOS 8 com Swift - 2. Mais Xcode e Swift, MVC

Enquanto seguia as palestras, cheguei a um ponto em que (até onde eu sabia) meu código era idêntico ao código do vídeo, mas no meu sistema recebi um erro do compilador. Após várias tentativas e erros, consegui reduzir meu código para dois exemplos, um dos quais gera um erro, o outro ou o que não, mas não tenho idéia do que está realmente causando o erro ou como resolvê-lo.

O código que cria o erro é:

import UIKit

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

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

Isso cria o seguinte erro do compilador:

Método 'execute' com o seletor de Objective-C 'execute:' entra em conflito com a declaração anterior com o mesmo seletor de Objective-C

Simplesmente removendo a subclasse de UIViewController, o código compila:

import UIKit

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

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

Algumas outras informações que podem ou não ser relevantes:

  • Eu atualizei recentemente para Yosemite.
  • Quando instalei o Xcode, acabei com uma versão beta (versão 6.3 (6D543q)) porque (se bem me lembro) era a versão que eu precisava executar na minha versão do OS X.

Eu meio que espero que isso seja um bug no compilador porque, caso contrário, isso não faz sentido para mim. Qualquer ajuda recebida com gratidão!

Auspice
fonte
3
Você pode executar o Xcode 6.2 no Yosemite. Você pode baixá-lo na loja de aplicativos e ele pode viver no seu sistema com a versão Beta. Eu não recomendaria o uso do Xcode 6.3 para a classe Stanford neste momento, porque ele é beta e inclui o Swift 1.2, que é diferente da versão anterior do Swift usada nos vídeos.
vacawama
2
A resposta (atualmente aceita) do usuário (fev) de 5 de abril não é mais a melhor. Em vez disso, a resposta de (James Zhang) de 16 de abril é mais específica e correta.
phlebotinum

Respostas:

144

O Objective-C não suporta sobrecarga de método, você deve usar um nome de método diferente. Quando você herdou o UIViewController, herdou o NSObject e tornou a classe interopável para Obj-C. O Swift, por outro lado, suporta sobrecarga, é por isso que funciona quando você remove a herança.

fevereiro
fonte
2
Substituição do método Objective-C SUPPORTS (com um con (de suprimível) avisos do compilador notificando sobre sobrecarregar algo já implementado), a Apple simplesmente não deseja que você faça isso para evitar que suas estruturas sejam sobrecarregadas. Estou usando essas sobrecargas fe UIFonttodos os dias.
26615 Michi
@ resposta de polarwar abaixo é o melhor para Swift 2: stackoverflow.com/a/31500740/144088
Crashalot
237

Eu também estou fazendo o curso Standford e fiquei preso aqui por um longo tempo também, mas depois de algumas pesquisas, encontrei algo daqui: notas de versão do Xcode e ele mencionou algo abaixo:

O Swift 1.2 é rigoroso quanto à verificação de sobrecarga baseada em tipo de métodos e inicializadores do @objc, algo não suportado pelo 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))
}

Esse código funcionaria quando invocado pelo Swift, mas poderia travar facilmente se invocado pelo Objective-C. Para resolver esse problema, use um tipo que não seja suportado pelo Objective-C para impedir que o compilador Swift exponha o membro ao tempo de execução do Objective-C:

  • Se fizer sentido, marque o membro como privado para desativar a inferência de @objc.
  • Caso contrário, use um parâmetro fictício com um valor padrão, por exemplo: _ nonobjc: () = (). (19826275)

Substituições de métodos expostos ao Objective-C em subclasses privadas não são inferidas como @objc, causando a falha do compilador Swift. Adicione explicitamente o atributo @objc a esses métodos de substituição. (19935352)

Os símbolos dos SDKs não estão disponíveis ao usar o Open Quickly em um projeto ou espaço de trabalho que usa Swift. (20349540)

o que eu fiz foi apenas adicionar "private" na frente do método de substituição como este:

    private func performOperation(operation: Double -> Double) {
    if operandStack.count >= 1 {
        displayValue = operation(operandStack.removeLast())
        enter()
    }
}
James Zhang
fonte
3
Esta solução é a mais viável Acho imho, pois isso totalmente faz sentido para definir este método privado
demental
38
Observe que agora também há um atributo @nonobjc, que pode ser usado para excluir um método do tempo de execução do Objective-C.
Erik J
2
Eu segundo comentário do ErikJ e resposta da polarwar abaixo. Esta parece ser a melhor resposta para avançar com o Swift 2 e o xcode 7. Se você ainda não atualizou, recomendo vivamente.
Austin A
@ resposta de polarwar abaixo é o melhor para Swift 2: stackoverflow.com/a/31500740/144088
Crashalot
111

Como já foi respondido, o ObjC não suporta sobrecarga de método (dois métodos com o mesmo nome) e no swift 2 no Xcode 7 existem duas opções para resolver esse tipo de problema. Uma opção é renomear o método usando o atributo:@objc(newNameMethod:)

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

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

Outra opção para resolver esse problema no Xcode 7+ é aplicar @nonobjcatributo a qualquer método, subscrito ou inicializador

func methodOne() {...}

@nonobjc
func methodOne() {...}
polarware
fonte
6
isso resolve o problema do swift 2 (e para cima). deve ser atualizado como a resposta mais correta. ty.
Maxim Veksler
2
Para qualquer um usando Swift 2 e Xcode 7 + esta é a resposta correta Concordo com polarwar
TerNovi
17

O problema é que UIViewControlleré uma @objcclasse. Ao herdar de UIViewController, BugViewControllertambém é uma @objcclasse.

Isso significa que ele deve estar em conformidade com as regras dos seletores de Objective-C (o nome de um método). Os métodos func perform(operation: (Double) -> Double)e func perform(operation: (Double, Double) -> Double)ambos têm o mesmo seletor @selector(perform:). Isso não é permitido.

Para resolver isso, use nomes diferentes: como func perform1(operation: (Double) -> Double)e func perform2(operation: (Double, Double) -> Double).


Eu acho que a melhor maneira de lidar com isso é dar aos seus perform()métodos nomes mais descritivos. O que esses métodos fazem? Como eles mudam o estado do controlador de exibição? Observe os outros UIViewControllermétodos para ter uma idéia do estilo de nomeação de métodos ou leia Nomes de métodos devem ser expressivos e exclusivos em uma classe

Jeffery Thomas
fonte
Obrigado - isso responde minha pergunta perfeitamente e, como você foi o primeiro, vou marcar isso como correto.
Auspice
Dito isto, ainda não entendo por que o código da palestra funcionou, pois tenho certeza de que fez o que meu código não compilador fez! Hey ho - eu vou voltar e verifique. Deve haver algo diferente.
Auspice
2
@Auspice Pode não ter produzido erros com a versão do Xcode que eles estavam usando para os vídeos, mas ainda era um problema. Não foi até o Xcode 6.3 que o compilador conseguiu detectar isso e avisá-lo.
Mick MacCallum
3
Paul Hegarty deseja demonstrar a função 'sobrecarga' aqui (duas funções com o mesmo nome, mas com um conjunto diferente de argumentos), então ele usa o mesmo nome de método de propósito! A sobrecarga é permitida apenas no Swift, não no Objective-C. É por isso que a solução é remover o formulário de herança UIViewController (que é uma classe Objective-C) ou declarar o método privado. Ambas as soluções são explicadas em detalhes nas outras respostas aqui.
Ronny Webers
Na verdade, eu usei a palavra-chave privada na frente da função. como, func pessoal performOperation (operação: Double -> Double) {} e func funcOperation privada (operação: (Double, Double) -> Double) {} Aqui eu consegui o método sobrecarregar com a ajuda de PRIVATE. porque eu usei os dois no ViewController.Swift apenas. Por que o compilador não diz nenhum erro?
iTag
2

Eu recebi o mesmo erro devido a ter dois métodos com a mesma assinatura Obj-C:

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

Eu não queria marcar um deles como @nonobjc devido à possibilidade de consequências imprevistas no tempo de execução. (Alguém pode me corrigir se não houver possibilidade)

Resolvi usando o recurso de nome de parâmetro externo do Swift (tornei o nome externo igual ao nome local) para o segundo método, que altera efetivamente a assinatura do método Obj-c:

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