Una declaración no puede ser un error 'final' y 'dinámico' en Swift 1.2

123

La declaración de valueabajo

import Foundation

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

provoca el siguiente error de compilación

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

¿Por qué sucede esto y cómo puedo lidiar con esto?

Estoy usando Swift 1.2 (la versión incluida en Xcode 6.3.1 6D1002)

eonil
fuente
La func test2declaración no es necesaria para activar el error, a partir de Xcode 7.3.1.
Rob Mayoff
1
Swift bug SR-993
rob mayoff
Simplemente ponga esa variable estática en otra estructura de nombres mejor
onmyway133

Respuestas:

224

Este problema surge porque Swift está intentando generar un acceso dinámico para la propiedad estática para la compatibilidad Obj-C, ya que la clase hereda de NSObject.

Si su proyecto está solo en Swift, en lugar de usar un vardescriptor de acceso, puede evitar el problema a través del @nonobjcatributo en Swift 2.0:

import Foundation

class AAA: NSObject {}
extension AAA {
    @nonobjc static let value = 111
}
Alex Pretzlav
fuente
Mi proyecto tiene algunos archivos Objective-C, pero ninguno de ese código interactúa con instancias de esta clase ( AAAaquí), ¿así que supongo que estoy en claro?
Nicolas Miari
Esta debería ser la respuesta seleccionada si usa una base de código Swift pura.
idzski
Estaba tratando de agregar variables estáticas (clase) a una NSManagedObjectsubclase. Esto lo solucionó!
Nicolas Miari
¿Soy el único que ha encontrado esta solución para arruinar completamente SourceKitService para Xcode 7.3?
NoodleOfDeath
57

Obtendrá este error si su clase cumple con estas condiciones.

  • Subclase de NSObject.
  • Tiene un static letcampo.
  • Accede al campo desde un método de instancia a través de dynamicType.

No sé por qué sucede esto, pero puedes probar esta solución.

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

O en forma más corta.

static var value: Int {
    return 111
}

Usar en static var { get }lugar de static let.


Aunque es muy probable que el optimizador de LLVM elimine el captador de propiedades y su costo de llamadas en el ejemplo anterior, es posible que desee evitarlo explícitamente.

Si le preocupa el costo de cálculo de dicho valor, puede crearlo una vez y almacenarlo en caché de esta manera.

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

O así si quieres ocultar la existencia de caché por completo.

static var value: Int {
    struct Local {
        static let cache = getTheNumber()
    }
    return Local.cache
}
eonil
fuente
55
Esto produce una propiedad calculada, que se volverá a calcular en cada acceso. Para este caso, puede que no importe demasiado, pero creo que vale la pena mencionarlo para que nadie use esta solución alternativa para objetos más grandes.
Nick Podratz
@NickPodratz ¿Sería una propiedad calculada también? private static let _value: Int = 111 static var value: Int { return _value }no tiene el get {pero el compilador menciona algo sobre la propiedad calculada si uso en varlugar delet
hashier
1
@hashier es. Dentro de las llaves se crea un cierre, geten este caso está implícito. Lo que puede hacer en su lugar es asignar el resultado del cierre de la variable de manera que el cierre se llama una sola vez: let value: Int = { return 111 }(). Los corchetes al final llaman al cierre. Pero tenga en cuenta que esta es una propiedad almacenada nuevamente y, por lo tanto, no está disponible en extensiones.
Nick Podratz
De acuerdo con la evaluación de @NickPodratz. Si bien esto resuelve el error que menciona el OP y, por lo tanto, lo convierte en una respuesta legítima, no proporciona ningún beneficio si desea que su variable sea realmente estática (que parece ser el punto). La respuesta de Alex es mejor en ese caso (suponiendo Swift puro)
Matt Long el
18

También tuve este error.

Mi problema era solo una var estática en una extensión rápida.

extension NotificationsViewController: UITableViewDataSource , UITableViewDelegate {

    static var timeIntervalFormatter = NSDateComponentsFormatter()

}

Moverlo a la implementación de clase resolvió el problema para mí.

JulianM
fuente
7

Acabo de tropezar con el mismo problema con una causa diferente y me gustaría publicarlo aquí para otras personas que experimentan el mismo mensaje de error inútil.

Una clase final que anula una variable calculada definida en una extensión también causa este error. Sin embargo, funciona para funciones y, por lo tanto, parece un error del compilador.

// 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
fuente
7

Resolví este problema moviendo la declaración estática a la nueva estructura que definí en la extensión.

Entonces, en lugar de esto:

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

Tengo esto:

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

Puede marcarlo como privado para evitar este error. Si desea exponerlo, puede envolverlo en una función pública:

extension AAA {

    private static let value = 111

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

En mi caso, solo hice referencia a la propiedad en la extensión misma, por lo que no había necesidad de exponerla.

pulse4life
fuente