Niedawno przeprowadziłem migrację kodu do Swift 4. Jest problem, z którym mam do czynienia rozszerzenia, tj
Deklaracje z rozszerzeń nie mogą być jeszcze nadpisane
Przeczytałem już wiele postów dotyczących tego wydania. Ale żaden z nich nie bawi się w scenariuszu opisanym poniżej:
class BaseCell: UITableViewCell
{
//Some code here...
}
extension BaseCell
{
func isValid() -> String?
{
//Some code here...
}
}
class SampleCell: BaseCell
{
//Some code here...
override func isValid() -> String? //ERROR..!!!
{
//Some code here...
}
}
Według Apple
Rozszerzenia mogą dodawać nowe funkcje do typu, ale nie mogą zastępować istniejących funkcji.
Ale w powyższym scenariuszu nie zastępuję metody isValid()
w rozszerzeniu. Jest zastępowany w SampleCell
sama definicja klasy. Mimo to daje błąd.
Odpowiedzi:
5 dla odpowiedzi № 1Ale w powyższym scenariuszu nie zastępuję metody
isValid()
w przedłużeniu.
isValid
zostanie zadeklarowany w pliku rozbudowa.
Błąd prawie mówi, że jeśli funkcja jest zadeklarowana w ten sposób, nie można tego zmienić.
Oświadczenie jest ważne dla obu z rozszerzenia i w rozszerzeniu.
2 dla odpowiedzi nr 2
Myślę, że to jest oczywiste. deklaracje OD rozszerzeń nie można jeszcze nadpisać
Próbujesz override
funkcja func isValid() -> String?
który został zadeklarowany w ramach extension
z BaseCell
, nie the BaseCell
sama klasa.
To wyraźnie mówi, że nie można przesłonić czegoś, co zostało zadeklarowane w rozszerzeniu.
Mam nadzieję, że to jest pomocne.
2 dla odpowiedzi nr 3
W Swift 3 mogłeś przesłonić funkcję rozszerzenia, jeśli rozszerzenie należało do klasy, która jest wyprowadzana z Objective-C (http://blog.flaviocaetano.com/post/this-is-how-to-override-extension-methods/), ale wydaje mi się, że teraz nie jest to możliwe w Swift 4. Możesz oczywiście zrobić coś takiego:
protocol Validity {
func isValid() -> String?
}
class BaseCell: UITableViewCell, Validity {
}
extension Validity
{
func isValid() -> String? {
return "false"
}
}
class SampleCell: BaseCell {
func isValid() -> String? {
return "true"
}
}
let base = BaseCell()
base.isValid() // prints false
let sample = SampleCell()
sample.isValid() // prints true
0 dla odpowiedzi nr 4
Jest nieważne w Swift, ale nie w Objective-C. Tak więc, jeśli Twoja sygnatura metody na to pozwala (brak zakazanych konstrukcji objc), możesz to zadeklarować @objc func myMethod()
i zastąpić go swobodnie w Swift.
0 dla odpowiedzi № 5
Ja też miałem ogromną spuściznę kodu Swift 3, który był używanyta stara sztuczka, aby osiągnąć to, co chciałem, więc kiedy przeniosłem się na Swift 4 i zacząłem otrzymywać te błędy, byłem nieco zdenerwowany. Nie bój się, jest rozwiązanie.
Ten błąd ma związek ze sposobem, w jaki Swift 4kompiluje klasy i nowy sposób, w jaki traktuje klasy i funkcje Objective-C. W Swift 3, jeśli klasa pochodzi z NSObject, wówczas wszystkie zmienne i funkcje w tej klasie używałyby konwencji dynamicznego nazewnictwa i wyszukiwania Objective-C. Takie podejście uniemożliwiło Swiftowi optymalizację kodu i poprawę wydajności kodu oraz rozmiar.
Aby przezwyciężyć te kary, w Swift 4 tylko zmienne i funkcje są wyraźnie oznaczone tagiem @objc
dostać traktowanie Objective-C, wszystko inne używa standardowych konwencji Swift: stąd błąd.
Uzbrojony w tę wiedzę, rozwiązaniem problemu jest oznaczenie funkcji w rozszerzeniu, które chcesz zastąpić @objc
, następnie w klasach potomnych zastąp funkcję, ale pamiętaj o dołączeniu rozszerzenia @objc
tag, aby Twój kod był wywoływany w czasie wykonywania.
OSTRZEŻENIE Jest tu trochę haczyka: jeśli zapomnisz dołączyć rozszerzenie @objc
w override
, kompilator nie będzie narzekał, ale w Twoim kodzie brakuje dynamicznego wyszukiwania, więc nigdy nie jest wywoływany w czasie wykonywania.
Twój kod powinien więc wyglądać mniej więcej tak:
class BaseCell: UITableViewCell {
//Some code here...
}
extension BaseCell {
@objc func isValid() -> String? {
//Some code here...
}
}
class SampleCell: BaseCell {
//Some code here...
@objc override func isValid() -> String? {
//Some code here...
}
}