Błąd kompilatora: Metoda z selektorem celu C powoduje konflikt z poprzednią deklaracją z tym samym selektorem celu C

209

Zaczynam uczyć się Swift i śledzę bardzo dobre wykłady wideo z Uniwersytetu Stanforda na YouTube. Oto link, jeśli jesteś zainteresowany lub pomaga (choć nie jest to konieczne do zrozumienia mojego problemu):

Tworzenie aplikacji dla systemu iOS 8 za pomocą Swift - 2. Więcej Xcode i Swift, MVC

Podczas wykładów dotarłem do punktu, w którym (o ile mogłem powiedzieć) mój kod był identyczny z kodem w filmie, ale w moim systemie wystąpił błąd kompilatora. Po wielu próbach i błędach udało mi się zredukować kod do dwóch przykładów, z których jeden generuje błąd, drugi lub który nie, ale nie mam pojęcia, co tak naprawdę powoduje błąd ani jak go rozwiązać.

Kod, który powoduje błąd to:

import UIKit

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

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

Powoduje to następujący błąd kompilatora:

Metoda „perform” z selektorem celu C „perform:” koliduje z poprzednią deklaracją z tym samym selektorem celu C

Po prostu usuwając podklasę UIViewController kod kompiluje:

import UIKit

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

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

Niektóre inne informacje, które mogą, ale nie muszą być istotne:

  • Niedawno uaktualniłem do Yosemite.
  • Kiedy zainstalowałem Xcode, skończyłem z wersją Beta (wersja 6.3 (6D543q)), ponieważ (jeśli dobrze pamiętam) była to wersja, którą musiałem uruchomić na mojej wersji OS X.

Mam nadzieję, że jest to błąd w kompilatorze, ponieważ w przeciwnym razie nie ma to dla mnie żadnego sensu. Każda pomoc otrzymana z wdzięcznością!

Łaska
źródło
3
Możesz uruchomić Xcode 6.2 na Yosemite. Możesz pobrać go ze sklepu z aplikacjami i może on żyć w twoim systemie z wersją Beta. W tym momencie nie polecam używania Xcode 6.3 dla klasy Stanford, ponieważ jest to wersja beta i zawiera Swift 1.2, który różni się od wcześniejszej wersji Swift używanej w filmach.
vacawama,
2
(Aktualnie akceptowana) odpowiedź od użytkownika (luty) z 5 kwietnia nie jest już najlepsza. Zamiast tego odpowiedź (James Zhang) z 16 kwietnia jest bardziej szczegółowa i poprawna.
phlebotinum,

Odpowiedzi:

144

Cel C nie obsługuje przeciążania metod, musisz użyć innej nazwy metody. Kiedy odziedziczyłeś UIViewController, odziedziczyłeś NSObject i uczyniłeś klasę współdziałającą z Obj-C. Z drugiej strony Swift obsługuje przeciążenie, dlatego działa po usunięciu dziedziczenia.

lut
źródło
2
Zastępowanie metody Objective-C SUPPORTS (z zestawem ostrzeżeń kompilatora (dających się tłumić) powiadamiających o przeciążeniu czegoś, co już zostało zaimplementowane), Apple po prostu nie chce, abyś to robił, aby uniknąć przeciążenia swoich frameworków. Używam takich przeciążeń na UIFontco dzień.
Michi,
Odpowiedź @ polarwar poniżej jest najlepsza dla Swift 2: stackoverflow.com/a/31500740/144088
Crashalot,
237

Ja również biorę udział w kursie w Standford i utknąłem tutaj również przez długi czas, ale po kilku poszukiwaniach znalazłem coś stąd: informacje o wydaniu Xcode i wspomniałem o tym poniżej:

Swift 1.2 ściśle sprawdza sprawdzanie przeciążenia opartego na typach metod @objc i inicjalizatorów, co nie jest obsługiwane przez 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))
}

Ten kod działałby po wywołaniu z Swift, ale mógłby łatwo ulec awarii, gdyby został wywołany z Objective-C. Aby rozwiązać ten problem, użyj typu, który nie jest obsługiwany przez Objective-C, aby uniemożliwić kompilatorowi Swift wystawienie członka na środowisko wykonawcze Objective-C:

  • Jeśli ma to sens, zaznacz członka jako prywatny, aby wyłączyć wnioskowanie o @objc.
  • W przeciwnym razie użyj parametru zastępczego z wartością domyślną, na przykład: _ nonobjc: () = (). (19826275)

Przesłonięcia metod narażonych na cel C w prywatnych podklasach nie są uznawane za @objc, co powoduje awarię kompilatora Swift. Jawnie dodaj atrybut @objc do takich nadrzędnych metod. (19935352)

Symbole z zestawów SDK nie są dostępne, gdy używasz Szybkiego otwierania w projekcie lub obszarze roboczym korzystającym z Swift. (20349540)

po prostu dodałem „private” przed metodą przesłonięcia w następujący sposób:

    private func performOperation(operation: Double -> Double) {
    if operandStack.count >= 1 {
        displayValue = operation(operandStack.removeLast())
        enter()
    }
}
James Zhang
źródło
3
To rozwiązanie jest najbardziej realne, jakie uważam za imho, ponieważ całkowicie ma sens ustawienie tej metody jako prywatnej
dementalna
38
Należy pamiętać, że teraz istnieje również atrybut @nonobjc, którego można użyć do wykluczenia metody z środowiska wykonawczego Objective-C.
Erik J,
2
Drugi komentarz @ ErikJ i odpowiedź polarwara poniżej. Wydaje się, że jest to najlepsza odpowiedź w przypadku Swift 2 i xcode 7. Jeśli jeszcze się nie zaktualizowałeś, zdecydowanie polecam.
Austin A,
Odpowiedź @ polarwar poniżej jest najlepsza dla Swift 2: stackoverflow.com/a/31500740/144088
Crashalot,
111

Jak już odpowiedziano, ObjC nie obsługuje przeciążania metod (dwie metody o tej samej nazwie), aw swift 2 pod Xcode 7 istnieją dwie opcje rozwiązania tego rodzaju problemów. Jedną z opcji jest zmiana nazwy metody za pomocą atrybutu:@objc(newNameMethod:)

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

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

inną opcją rozwiązania tego problemu w Xcode 7+ jest zastosowanie @nonobjcatrybutu do dowolnej metody, indeksu dolnego lub inicjatora

func methodOne() {...}

@nonobjc
func methodOne() {...}
polarware
źródło
6
rozwiązuje to problem dla szybkiego 2 (i więcej). należy zaktualizować jako najbardziej poprawną odpowiedź. ty.
Maxim Veksler
2
Dla każdego, kto używa Swift 2 i Xcode 7 +, jest to poprawna odpowiedź Zgadzam się z polarwar
TerNovi
17

IS problemem UIViewControllerjest @objcklasa. Dziedzicząc po UIViewController, BugViewControllerjest również @objcklasą.

Oznacza to, że musi być zgodny z regułami selektorów Objective-C (nazwa metody). Metody func perform(operation: (Double) -> Double)i func perform(operation: (Double, Double) -> Double)obie mają ten sam selektor @selector(perform:). To jest niedozwolone.

Aby rozwiązać ten problem, użyj różnych nazw: jak func perform1(operation: (Double) -> Double)i func perform2(operation: (Double, Double) -> Double).


Myślę, że najlepszym sposobem na poradzenie sobie z tym jest nadanie perform()metodom bardziej opisowych nazw. Co robią te metody? Jak zmieniają stan kontrolera widoku? Spójrz na inne UIViewControllermetody, aby poczuć styl nazewnictwa metod, lub przeczytaj Nazwy metod powinny być wyraziste i unikalne w obrębie klasy

Jeffery Thomas
źródło
Dzięki - to doskonale odpowiada na moje pytanie i jako pierwszy zaznaczę to jako poprawne.
Auspice
Powiedziawszy, że nadal nie rozumiem, dlaczego kod wykładu działał, jestem pewien, że zrobił to, co zrobił mój niekompilujący się kod! Hej ho - wrócę i jeszcze raz to sprawdzę. Musi być coś innego.
Auspice
2
@Auspicja Być może nie wystąpiły błędy w wersji Xcode, której używali do filmów, ale nadal był to problem. Dopiero w Xcode 6.3 kompilator był w stanie to wykryć i ostrzec.
Mick MacCallum,
3
Paul Hegarty chce tutaj zademonstrować „przeciążenie” funkcji (2 funkcje o tej samej nazwie, ale z innym zestawem argumentów), więc celowo używa tej samej nazwy metody! Przeładowanie jest dozwolone tylko w Swift, a nie w Objective-C. Dlatego rozwiązaniem jest albo usunięcie dziedziczenia z UIViewController (która jest klasą Objective-C), albo zadeklarowanie metody jako prywatnej. Oba rozwiązania zostały szczegółowo wyjaśnione w innych odpowiedziach tutaj.
Ronny Webers,
Właściwie użyłem prywatnego słowa kluczowego przed funkcją. jak prywatny func performOperation (operacja: Double -> Double) {} i prywatny func performOperation (operacja: (Double, Double) -> Double) {} Tutaj osiągnąłem przeciążenie metody przy pomocy PRIVATE. ponieważ użyłem ich obu tylko w ViewController.Swift. Dlaczego kompilator nie wyświetla błędu?
iTag
2

Wystąpił ten sam błąd, ponieważ mam dwie metody z tym samym podpisem Obj-C:

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

Nie chciałem oznaczać jednego z nich jako @nonobjc ze względu na możliwość nieprzewidzianych konsekwencji w czasie wykonywania. (Ktoś może mnie poprawić, jeśli nie ma takiej możliwości)

Rozwiązano go za pomocą funkcji zewnętrznej nazwy parametru Swift (nazwa zewnętrzna jest taka sama jak nazwa lokalna) do drugiej metody, która skutecznie zmienia sygnaturę metody Obj-c:

static func prepareForUpSyncing(objs objs : [NSManagedObject]!) -> Bool {
Protongun
źródło