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)


func test2Deklaracja nie jest potrzebne, aby wywołać błąd, ponieważ z Xcode 7.3.1.
— 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
}

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
}

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.


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 }
}

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                
            }()
    }
}

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ć.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.