Deklaracja nie może być jednocześnie błędem „końcowym” i „dynamicznym” w Swift 1.2

123

Oświadczenie valueponiżej

import Foundation

class AAA: NSObject {
    func test2() {
        self.dynamicType
    }
}
extension AAA {
    static let value    =   111
}

powoduje następujący błąd kompilacji

A declaration cannot be both 'final' and 'dynamic'

Dlaczego tak się dzieje i jak sobie z tym radzić?

Używam Swift 1.2 (wersja dostarczana z Xcode 6.3.1 6D1002)

eonil
źródło
func test2Deklaracja nie jest potrzebne, aby wywołać błąd, ponieważ z Xcode 7.3.1.
rob mayoff
1
Szybki błąd SR-993
rob mayoff
Po prostu umieść tę statyczną zmienną w innej lepszej strukturze nazewnictwa
onmyway133

Odpowiedzi:

224

Ten problem występuje, ponieważ Swift próbuje wygenerować dynamiczny akcesor dla właściwości statycznej w celu zapewnienia zgodności z Obj-C, ponieważ klasa dziedziczy z NSObject.

Jeśli Twój projekt jest tylko w języku Swift, zamiast używać varakcesorium, możesz uniknąć problemu za pomocą @nonobjcatrybutu w języku Swift 2.0:

import Foundation

class AAA: NSObject {}
extension AAA {
    @nonobjc static let value = 111
}
Alex Pretzlav
źródło
Mój projekt ma kilka plików Objective-C, ale żaden z tych kodów nie wchodzi w interakcje z wystąpieniami tej klasy ( AAAtutaj), więc myślę, że wszystko jest jasne?
Nicolas Miari
To powinna być wybrana odpowiedź, jeśli używasz czystej bazy kodu Swift.
idzski
Próbowałem dodać zmienne statyczne (klasy) do NSManagedObjectpodklasy. To naprawiło!
Nicolas Miari
Czy jestem jedynym, który znalazł tę poprawkę, aby całkowicie schrzanić SourceKitService dla Xcode 7.3?
NoodleOfDeath
57

Ten błąd zostanie wyświetlony, jeśli Twoja klasa spełnia te warunki.

  • Podklasa z NSObject.
  • Ma static letpole.
  • Uzyskuje dostęp do pola z metody instancji za pośrednictwem dynamicType.

Nie wiem, dlaczego tak się dzieje, ale możesz wypróbować to obejście.

static var value: Int {
    get {
        return 111
    }
}

Lub w krótszej formie.

static var value: Int {
    return 111
}

Użyj static var { get }zamiast static let.


Chociaż funkcja pobierająca właściwość i jej koszt wywołania z dużym prawdopodobieństwem zostaną wyeliminowane przez optymalizator LLVM w powyższym przykładzie, możesz chcieć jawnie tego uniknąć.

Jeśli obawiasz się takiego kosztu obliczenia wartości, możesz go utworzyć raz i zapisać w pamięci podręcznej w ten sposób.

static var value: Int {
    return cache
}
private let cache = getTheNumber()

Lub w ten sposób, jeśli chcesz całkowicie ukryć istnienie pamięci podręcznej.

static var value: Int {
    struct Local {
        static let cache = getTheNumber()
    }
    return Local.cache
}
eonil
źródło
5
Tworzy to obliczoną właściwość, która będzie ponownie obliczana przy każdym dostępie. W tym przypadku może to nie mieć większego znaczenia, ale myślę, że warto o tym wspomnieć, aby nikt nie stosował tego obejścia dla większych obiektów.
Nick Podratz
@NickPodratz czy byłaby to również właściwość obliczeniowa? private static let _value: Int = 111 static var value: Int { return _value }nie ma, get {ale kompilator wspomina coś o właściwości obliczonej, jeśli varlet
użyję
1
@hashier to jest. Wewnątrz nawiasów klamrowych tworzysz zamknięcie, getw tym przypadku jest to niejawne. Co można zrobić, a nie tylko przypisać wynikiem zamknięcia do zmiennej tak, że zamknięcie jest wywoływana tylko raz: let value: Int = { return 111 }(). Nawiasy na końcu nazywają zamknięcie. Należy jednak pamiętać, że jest to ponownie przechowywana właściwość i dlatego nie jest dostępna w rozszerzeniach.
Nick Podratz
Zgadzam się z oceną @NickPodratz. Chociaż rozwiązuje to błąd, o którym wspomina OP, i dlatego jest to poprawna odpowiedź, nie zapewnia żadnej korzyści, jeśli chcesz, aby zmienna faktycznie była statyczna (co wydaje się być celem). Odpowiedź Alexa jest lepsza w tym przypadku (zakładając czysty Swift)
Matt Long
18

Ja też miałem ten błąd.

Mój problem był po prostu statyczną zmienną w szybkim rozszerzeniu.

extension NotificationsViewController: UITableViewDataSource , UITableViewDelegate {

    static var timeIntervalFormatter = NSDateComponentsFormatter()

}

Przeniesienie go do implementacji klasy rozwiązało problem za mnie.

JulianM
źródło
7

Właśnie natknąłem się na ten sam problem z inną przyczyną i chciałbym opublikować go tutaj dla innych osób, które mają ten sam bezużyteczny komunikat o błędzie.

Ostatnia klasa, która przesłania obliczoną zmienną zdefiniowaną w rozszerzeniu, również powoduje ten błąd. Działa jednak dla funkcji i dlatego wygląda jak błąd kompilatora.

// at line 0: a declaration cannot be both 'final' and 'dynamic'

import UIKit

extension UIViewController {
    var test: Int { return 0 }
}

final class TestController: UIViewController {
    override var test: Int { return 1 }
}
płynny
źródło
7

Rozwiązałem ten problem, przenosząc deklarację statyczną do nowej struktury, którą zdefiniowałem w rozszerzeniu.

Więc zamiast tego:

extension NSOperationQueue {
    static var parsingQueue : NSOperationQueue = {
        let queue = NSOperationQueue()
        queue.maxConcurrentOperationCount = 1
        return queue
        }()
}

Mam to:

extension NSOperationQueue {        
    struct Shared {
        static var parsingQueue : NSOperationQueue = {
            let queue = NSOperationQueue()
            queue.maxConcurrentOperationCount = 1
            return queue                
            }()
    }
}
VojtaStavik
źródło
0

Możesz oznaczyć go jako prywatny, aby zapobiec występowaniu tego błędu. Jeśli chcesz go ujawnić, możesz umieścić go w funkcji publicznej:

extension AAA {

    private static let value = 111

    public func getDatValue() -> Int {
        return AAA.value
    }    
}

W moim przypadku odwołałem się do właściwości tylko w samym rozszerzeniu, więc nie było potrzeby, aby go ujawniać.

Pulse4life
źródło