Jak mogę sobie poradzić z wycofywaniem @objc wnioskowania z #selector () w Swift 4?

132

Próbuję przekonwertować kod źródłowy mojego projektu ze Swift 3 na Swift 4. Jedno ostrzeżenie, które daje mi Xcode, dotyczy moich selektorów.

Na przykład dodaję cel do przycisku za pomocą zwykłego selektora, takiego jak ten:

button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)

To jest ostrzeżenie, które pokazuje:

Argument „#selector” odnosi się do metody instancji „myAction ()” w „ViewController”, która zależy od wnioskowania o atrybucie „@objc”, które jest przestarzałe w Swift 4

Dodaj „@objc”, aby uwidocznić tę metodę wystąpienia w Objective-C

Teraz kliknięcie Fixna komunikat o błędzie robi to dla mojej funkcji:

// before
func myAction() { /* ... */ }

// after
@objc func myAction() { /* ... */ }

Naprawdę nie chcę zmieniać nazw wszystkich moich funkcji, aby zawierały @objcznak i zakładam, że nie jest to konieczne.

Jak przepisać selektor, aby poradzić sobie z wycofaniem?


Powiązane pytanie:

LinusGeffarth
źródło
3
Nie, ich oznakowanie @objc jest już konieczne, aby wystawiać na działanie obj-C, a zatem użycia z przełączników.
Hamish
3
Więc przestarzała część wnioskuje o funkcjach dostępu publicznego jako @objc? To trochę denerwujące, ale generalnie ustawiam te funkcje jako prywatne, wymagając ode mnie oznaczenia ich jako @objctak czy inaczej.
Connor
2
Zobacz SE-0160, aby uzyskać więcej informacji na temat zmiany. Inną alternatywą jest oznaczenie danej klasy jako @objcMembersw celu wystawienia wszystkich członków zgodnych z Obj-C na działanie Obj-C, ale nie radziłbym tego, chyba że faktycznie potrzebujesz całej swojej klasy do ujawnienia.
Hamish
3
@LinusGeffarth Jak wspomniano w propozycji, prawdopodobnie niepotrzebnie zwiększyłoby to rozmiar twojego pliku binarnego, a linkowanie dynamiczne potrwa dłużej. Naprawdę nie sądzę, by było to zbyt kłopotliwe dla dodatkowej przejrzystości, którą konkretnie masz na myśli, aby użyć określonej rzeczy z Obj-C.
Hamish
1
Próbowałem, nie działa.
LinusGeffarth

Odpowiedzi:

156

Poprawka jest poprawna - nie ma nic w selektorze, który możesz zmienić, aby metoda, do której się odnosi, była ujawniona w celu-C.

Cały powód tego ostrzeżenia w pierwszej kolejności wynika z SE-0160 . Przed Swift 4 internallub nowszym NSObjectskładowe klas dziedziczących zgodne z Objective-C były wywnioskowane, @objca tym samym narażone na Objective-C, dzięki czemu można je wywoływać za pomocą selektorów (ponieważ środowisko uruchomieniowe Obj-C jest wymagane do wyszukania metody implementacja dla danego selektora).

Jednak w Swift 4 już tak nie jest. Obecnie tylko bardzo specyficzne deklaracje są wywnioskowane jako @objcna przykład przesłonięcia @objcmetod, implementacje @objcwymagań protokołu i deklaracje z atrybutami, które implikują @objc, takie jak @IBOutlet.

Motywacją do tego, jak opisano szczegółowo w powyższej połączonej propozycji , jest po pierwsze zapobieganie NSObjectkolizji przeciążeń metod w dziedziczeniu klas ze względu na posiadanie identycznych selektorów. Po drugie, pomaga zmniejszyć rozmiar binarny, ponieważ nie musi generować zwrotów dla członków, którzy nie muszą być narażeni na Obj-C, a po trzecie poprawia prędkość dynamicznego łączenia.

Jeśli chcesz wystawić członka na Obj-C, musisz oznaczyć go jako @objcna przykład:

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        button.addTarget(self, action: #selector(foo), for: .touchUpInside)
    }

    @objc func foo() {
       // ... 
    }
}

(migrator powinien to zrobić automatycznie z selektorami, gdy jest uruchomiony z wybraną opcją „minimalizuj wnioskowanie”)

Aby wystawić grupę członków na Obj-C, możesz użyć @objc extension:

@objc extension ViewController {

    // both exposed to Obj-C
    func foo() {}
    func bar() {}
}

Spowoduje to ujawnienie wszystkich zdefiniowanych w nim elementów członkowskich Obj-C i spowoduje wyświetlenie błędu w przypadku wszystkich elementów członkowskich, których nie można ujawnić Obj-C (chyba że wyraźnie oznaczono jako @nonobjc).

Jeśli masz klasę, w której chcesz, aby wszyscy członkowie zgodni z Obj-C byli narażeni na działanie Obj-C, możesz oznaczyć klasę jako @objcMembers:

@objcMembers
class ViewController: UIViewController {
   // ...
}

Teraz wszyscy członkowie, o których można wywnioskować, @objcże są. Jednak nie radziłbym tego robić, chyba że naprawdę potrzebujesz wszystkich członków narażonych na Obj-C, biorąc pod uwagę wyżej wymienione wady niepotrzebnego ujawniania członków.

Hamish
źródło
2
Dlaczego Xcode nie robi tego automatycznie podczas konwersji kodu do najnowszej składni?
LinusGeffarth
1
Zastanawiam się, czy @IBActionteż są automatycznie @objc? tak nie było do tej pory, ale byłoby to logiczne. EDYCJA: nvm, propozycja jasno stwierdza, że @IBActionwystarczy, aby metoda była @objc.
Sulthan
8
Nic z tego nie rozumiem. Nie mam kodu Objective C, co tak zawsze. Czy nie ma czystego szybkiego sposobu dodawania celów do przycisku? Lub użyć selektorów? Nie chcę, aby mój kod był pełen tego atrybutu. Czy to dlatego, że UIButton pochodzi z jakiegoś NSObject / Obj-c-thing?
Sti
11
@Sti Tak więc, aby bezpośrednio odpowiedzieć „ Czy nie ma czystego szybkiego sposobu dodawania celów do przycisku? Lub użyj selektorów? ” - nie, nie ma „czystego Swift” sposobu używania selektorów do wysyłania do metod. Opierają się na środowisku wykonawczym Obj-C w celu wyszukania implementacji metody w celu wywołania określonego selektora - środowisko wykonawcze Swift nie ma takiej możliwości.
Hamish
12
Selektory mężczyzn to bałagan. Wygląda na to, że każda inna szybka aktualizacja się z tym miesza. Dlaczego nie możemy po prostu mieć czystego, szybkiego selektora autouzupełniania dla metod. Sprawia, że ​​kod wygląda tak brzydko.
John Riselvato
16

Jako oficjalna dokumentacja Apple . musisz użyć @objc, aby wywołać metodę selektora.

W celu-C selektor jest typem, który odwołuje się do nazwy metody celu-C. W języku Swift selektory celu-C są reprezentowane przez Selectorstrukturę i można je skonstruować za pomocą #selector wyrażenia. Aby utworzyć selektor dla metody, którą można wywołać z Objective-C, przekaż nazwę metody, na przykład #selector(MyViewController.tappedButton(sender:)). Aby skonstruować selektor dla metody pobierającej lub ustawiającej Objective-C właściwości, należy przekazać nazwę właściwości poprzedzoną etykietą getter:lub setter:, taką jak #selector(getter: MyViewController.myButton).

Kiran Sarvaiya
źródło
wszystko, co z tego rozumiem, to to, że w niektórych przypadkach muszę dodać @objc na początku funkcji, niezależnie od tego, czy kiedykolwiek będę ją wywoływać z Objective-C. Po prostu uczę się Swifta, używając Obj-C przez lata
SundialSoft,
10

Od, myślę, że Swift 4.2, wszystko, co musisz zrobić, to przypisać @IBAction do swojej metody i możesz uniknąć tej głupiej adnotacji @objc

`` ''

let tap  =  UITapGestureRecognizer(target: self, action: #selector(self.cancel))


@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}
Logan Sease
źródło
1
To również powie konstruktorowi interfejsu, że ta funkcja może być podłączona, co może nie być tym, czego chcesz. Robi to samo, co @objc.
Josh Paradroid
2

Jak już wspomniano w innych odpowiedziach, nie ma sposobu, aby uniknąć @objcadnotacji dla selektorów.

Ale ostrzeżenie wspomniane w PO można wyciszyć, wykonując następujące kroki:

  1. Przejdź do ustawień kompilacji
  2. Wyszukaj słowo kluczowe @objc
  3. Ustaw wartość interfejsu Swift 3 @objc naOff

poniżej znajduje się zrzut ekranu, który ilustruje powyższe kroki:

Wyciszanie ostrzeżenia „Swift 3 @objc interface”

Mam nadzieję że to pomoże

S1LENT WARRIOR
źródło
Jak to wpływa na Twój projekt w szerszej perspektywie?
Zonily Jame,
1
A co z rozmiarem * .ipa lub * .xarchive i czasem kompilacji?
Zonily Jame,
@ZonilyJame Nie zauważyłem czasu kompilacji, ale nie miało to wpływu na rozmiar IPA
S1LENT WARRIOR
Nie jest to zalecana wartość, której możesz użyć, jeśli używasz wersji Swift 4.0+ zgodnie z wytycznymi Apple. Sprawdź tę stronę help.apple.com/xcode/mac/current/#/deve838b19a1
Bhavin_m
1
Możesz zaktualizować tę odpowiedź, ustawiając wartość Default w Xcode 11. Ważne jest, aby ustawić wartość zarówno w projekcie, jak i celach, aby całkowicie usunąć ostrzeżenie.
Tommie C.
2

Jeśli potrzebujesz obiektywnych członków c w kontrolerze widoku, po prostu dodaj @objcMembers w górnej części kontrolera widoku. Możesz tego uniknąć, dodając IBAction do swojego kodu.

@IBAction func buttonAction() {

}

Upewnij się, że podłączasz to gniazdko w scenorysie.

Renjish C
źródło