Une déclaration ne peut pas être à la fois une erreur `` finale '' et une erreur `` dynamique '' dans Swift 1.2

123

La déclaration valueci - dessous

import Foundation

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

provoque l'erreur de compilation suivante

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

Pourquoi cela se produit-il et comment puis-je y faire face?

J'utilise Swift 1.2 (la version livrée dans Xcode 6.3.1 6D1002)

éonil
la source
La func test2déclaration n'est pas nécessaire pour déclencher l'erreur, à partir de Xcode 7.3.1.
rob mayoff
1
Swift bug SR-993
rob mayoff
Il suffit de mettre cette variable statique dans une autre meilleure structure de nommage
onmyway133

Réponses:

224

Ce problème survient parce que Swift essaie de générer un accesseur dynamique pour la propriété statique pour la compatibilité Obj-C, puisque la classe hérite de NSObject.

Si votre projet est uniquement dans Swift, plutôt que d'utiliser un varaccesseur, vous pouvez éviter le problème via l' @nonobjcattribut dans Swift 2.0:

import Foundation

class AAA: NSObject {}
extension AAA {
    @nonobjc static let value = 111
}
Alex Pretzlav
la source
Mon projet contient des fichiers Objective-C, mais aucun de ce code n'interagit avec les instances de cette classe ( AAAici), donc je suppose que je suis en clair?
Nicolas Miari
Cela devrait être la réponse choisie si vous utilisez une base de code Swift pure.
idzski
J'essayais d'ajouter des variables statiques (de classe) à une NSManagedObjectsous-classe. Cela l'a corrigé!
Nicolas Miari
Suis-je le seul à avoir trouvé ce correctif pour bousiller complètement SourceKitService pour Xcode 7.3?
NoodleOfDeath
57

Vous obtiendrez cette erreur si votre classe remplit ces conditions.

  • Sous-classé à partir de NSObject.
  • A un static letchamp.
  • Accède au champ à partir d'une méthode d'instance via dynamicType.

Je ne sais pas pourquoi cela se produit, mais vous pouvez essayer cette solution de contournement.

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

Ou sous une forme plus courte.

static var value: Int {
    return 111
}

Utilisez à la static var { get }place de static let.


Bien que le getter de propriété et son coût d'appel soient très probablement éliminés par l'optimiseur LLVM dans l'exemple ci-dessus, vous voudrez peut-être l'éviter explicitement.

Si vous êtes préoccupé par ce coût de calcul de valeur, vous pouvez le créer une fois et le mettre en cache comme celui-ci.

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

Ou comme ça si vous souhaitez masquer complètement l'existence du cache.

static var value: Int {
    struct Local {
        static let cache = getTheNumber()
    }
    return Local.cache
}
éonil
la source
5
Cela produit une propriété calculée, qui sera recalculée à chaque accès. Dans ce cas, cela n'a peut-être pas trop d'importance, mais je pense que cela vaut la peine de le mentionner afin que personne n'utilise cette solution de contournement pour les objets plus gros.
Nick Podratz
@NickPodratz serait-il également une propriété calculée? private static let _value: Int = 111 static var value: Int { return _value }il n'a pas le get {mais le compilateur mentionne quelque chose à propos de la propriété calculée si j'utilise à la varplace delet
hashier
1
@hashier ça l'est. À l'intérieur des accolades, vous créez une fermeture, getdans ce cas implicite. Ce que vous pouvez faire à la place est le résultat de assign la fermeture à la variable de telle sorte que la fermeture est appelée une seule fois: let value: Int = { return 111 }(). Les crochets à la fin appellent la fermeture. Mais sachez qu'il s'agit à nouveau d'une propriété stockée et donc non disponible dans les extensions.
Nick Podratz
D'accord avec l'évaluation de @NickPodratz. Bien que cela résout l'erreur mentionnée par l'OP et en fait donc une réponse légitime, cela ne présente aucun avantage si vous souhaitez que votre variable soit réellement statique (ce qui semble être le point). La réponse d'Alex est meilleure dans ce cas (en supposant que Swift pur)
Matt Long
18

J'ai eu cette erreur aussi.

Mon problème était juste un var statique dans une extension rapide.

extension NotificationsViewController: UITableViewDataSource , UITableViewDelegate {

    static var timeIntervalFormatter = NSDateComponentsFormatter()

}

Le déplacer vers l'implémentation de classe a résolu le problème pour moi.

JulianM
la source
7

Je viens de trébucher sur le même problème avec une cause différente et je voudrais le publier ici pour d'autres personnes rencontrant le même message d'erreur inutile.

Une classe finale qui remplace une variable calculée définie dans une extension provoque également cette erreur. Cela fonctionne pour les fonctions et ressemble donc à un bogue du compilateur.

// 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
la source
7

J'ai résolu ce problème en déplaçant la déclaration statique dans la nouvelle structure que j'ai définie dans l'extension.

Donc au lieu de ça:

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

J'ai ceci:

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

Vous pouvez le marquer comme privé pour éviter cette erreur. Si vous souhaitez l'exposer, vous pouvez l'envelopper dans une fonction publique:

extension AAA {

    private static let value = 111

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

Dans mon cas, je n'ai référencé la propriété que dans l'extension elle-même, il n'était donc pas nécessaire de l'exposer.

pulse4life
la source