Eine Deklaration kann in Swift 1.2 nicht sowohl ein "endgültiger" als auch ein "dynamischer" Fehler sein

123

Die Erklärung von valueunten

import Foundation

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

verursacht den folgenden Kompilierungsfehler

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

Warum passiert das und wie kann ich damit umgehen?

Ich verwende Swift 1.2 (die in Xcode 6.3.1 6D1002 enthaltene Version)

eonil
quelle
Die func test2Deklaration wird ab Xcode 7.3.1 nicht benötigt, um den Fehler auszulösen.
Rob Mayoff
1
Schneller
Fügen Sie

Antworten:

224

Dieses Problem tritt auf, weil Swift versucht, einen dynamischen Accessor für die statische Eigenschaft für die Obj-C-Kompatibilität zu generieren, da die Klasse von erbt NSObject.

Wenn sich Ihr Projekt nur in Swift befindet, anstatt einen varAccessor zu verwenden, können Sie das Problem über das @nonobjcAttribut in Swift 2.0 vermeiden :

import Foundation

class AAA: NSObject {}
extension AAA {
    @nonobjc static let value = 111
}
Alex Pretzlav
quelle
Mein Projekt hat einige Objective-C-Dateien, aber keiner dieser Codes interagiert mit Instanzen dieser Klasse ( AAAhier), also bin ich wohl im klaren?
Nicolas Miari
Dies sollte die ausgewählte Antwort sein, wenn eine reine Swift-Codebasis verwendet wird.
Idzski
Ich habe versucht, einer NSManagedObjectUnterklasse statische (Klassen-) Variablen hinzuzufügen . Das hat es behoben!
Nicolas Miari
Bin ich der einzige, der dieses Update gefunden hat, um SourceKitService für Xcode 7.3 komplett zu vermasseln?
NoodleOfDeath
57

Sie erhalten diesen Fehler, wenn Ihre Klasse diese Bedingungen erfüllt.

  • Unterklasse von NSObject.
  • Hat ein static letFeld.
  • Zugriff auf das Feld von einer Instanzmethode über dynamicType.

Ich weiß nicht, warum dies passiert, aber Sie können diese Problemumgehung ausprobieren.

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

Oder in kürzerer Form.

static var value: Int {
    return 111
}

Verwenden Sie static var { get }anstelle von static let.


Obwohl Property Getter und seine Aufrufkosten im obigen Beispiel sehr wahrscheinlich vom LLVM-Optimierer eliminiert werden, möchten Sie dies möglicherweise explizit vermeiden.

Wenn Sie über solche Wertberechnungskosten besorgt sind, können Sie sie einmal erstellen und so zwischenspeichern.

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

Oder so, wenn Sie die Existenz des Caches vollständig verbergen möchten.

static var value: Int {
    struct Local {
        static let cache = getTheNumber()
    }
    return Local.cache
}
eonil
quelle
5
Dies erzeugt eine berechnete Eigenschaft, die bei jedem Zugriff neu berechnet wird. In diesem Fall ist es vielleicht nicht so wichtig, aber ich denke, es ist erwähnenswert, damit niemand diese Problemumgehung für größere Objekte verwendet.
Nick Podratz
@NickPodratz wäre dies auch eine berechnete Eigenschaft? private static let _value: Int = 111 static var value: Int { return _value }es hat nicht das, get {aber der Compiler erwähnt etwas über berechnete Eigenschaft, wenn ich varanstelle vonlet
Hashier
1
@ Hashier ist es. Innerhalb der geschweiften Klammern erstellen Sie einen Verschluss, der getin diesem Fall implizit ist. Sie können stattdessen das Ergebnis des Abschlusses der Variablen zuweisen, sodass der Abschluss nur einmal aufgerufen wird : let value: Int = { return 111 }(). Die Klammern am Ende rufen den Verschluss auf. Beachten Sie jedoch, dass dies wieder eine gespeicherte Eigenschaft ist und daher in Erweiterungen nicht verfügbar ist.
Nick Podratz
Stimmen Sie der Einschätzung von @NickPodratz zu. Dies behebt zwar den Fehler, den das OP erwähnt, und macht dies daher zu einer legitimen Antwort. Es bietet jedoch keinen Vorteil, wenn Sie möchten, dass Ihre Variable tatsächlich statisch ist (was der Punkt zu sein scheint). Alex 'Antwort ist in diesem Fall besser (unter der Annahme von reinem Swift)
Matt Long
18

Ich hatte auch diesen Fehler.

Mein Problem war nur eine statische Variable in einer schnellen Erweiterung.

extension NotificationsViewController: UITableViewDataSource , UITableViewDelegate {

    static var timeIntervalFormatter = NSDateComponentsFormatter()

}

Das Verschieben in die Klassenimplementierung hat das Problem für mich gelöst.

JulianM
quelle
7

Ich bin gerade über dasselbe Problem mit einer anderen Ursache gestolpert und möchte es hier für andere Personen veröffentlichen, bei denen dieselbe nutzlose Fehlermeldung auftritt.

Eine letzte Klasse, die eine in einer Erweiterung definierte berechnete Variable überschreibt, verursacht ebenfalls diesen Fehler. Es funktioniert jedoch für Funktionen und sieht daher wie ein Compiler-Fehler aus.

// 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 }
}
Fluidsonic
quelle
7

Ich habe dieses Problem gelöst, indem ich die statische Deklaration in die neue Struktur verschoben habe, die ich in der Erweiterung definiert habe.

Also stattdessen:

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

Ich habe das:

extension NSOperationQueue {        
    struct Shared {
        static var parsingQueue : NSOperationQueue = {
            let queue = NSOperationQueue()
            queue.maxConcurrentOperationCount = 1
            return queue                
            }()
    }
}
VojtaStavik
quelle
0

Sie können es als privat markieren, um diesen Fehler zu vermeiden. Wenn Sie es verfügbar machen möchten, können Sie es in eine öffentliche Funktion einschließen:

extension AAA {

    private static let value = 111

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

In meinem Fall habe ich nur auf die Eigenschaft in der Erweiterung selbst verwiesen, sodass sie nicht verfügbar gemacht werden musste.

puls4life
quelle
0

Als leichte Verbesserung gegenüber @ Eonils Antwort ist das getnicht notwendig:

static var value: Int { return  111 }
Yuchen Zhong
quelle