Uma declaração não pode ser um erro 'final' e 'dinâmico' no Swift 1.2

123

A declaração valueabaixo

import Foundation

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

causa o seguinte erro de compilação

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

Por que isso acontece e como posso lidar com isso?

Estou usando o Swift 1.2 (a versão fornecida no Xcode 6.3.1 6D1002)

eonil
fonte
A func test2declaração não é necessária para acionar o erro, a partir do Xcode 7.3.1.
rob mayoff
1
Swift bug SR-993
rob mayoff
Basta colocar essa variável estática para outra struct melhor nomeando
onmyway133

Respostas:

224

Esse problema ocorre porque o Swift está tentando gerar um acessador dinâmico para a propriedade estática para compatibilidade com Obj-C, já que a classe é herdada NSObject.

Se o seu projeto estiver apenas no Swift, em vez de usar um varacessador, você poderá evitar o problema pelo @nonobjcatributo no Swift 2.0:

import Foundation

class AAA: NSObject {}
extension AAA {
    @nonobjc static let value = 111
}
Alex Pretzlav
fonte
Meu projeto tem alguns arquivos Objective-C, mas nenhum desse código interage com instâncias dessa classe ( AAAaqui), então acho que estou livre?
Nicolas Miari
Essa deve ser a resposta selecionada se você estiver usando uma base de código Swift pura.
Idzski
Eu estava tentando adicionar vars estáticos (de classe) a uma NSManagedObjectsubclasse. Isso consertou!
Nicolas Miari 23/02
Eu sou o único que encontrou essa correção para estragar completamente o SourceKitService for Xcode 7.3?
NoodleOfDeath
57

Você receberá esse erro se sua turma atender a essas condições.

  • Subclassificado de NSObject.
  • Tem um static letcampo
  • Acessa o campo a partir de um método de instância via dynamicType.

Não sei por que isso acontece, mas você pode tentar esta solução alternativa.

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

Ou em forma mais curta.

static var value: Int {
    return 111
}

Use em static var { get }vez de static let.


Embora o getter de propriedades e seu custo de chamada provavelmente sejam eliminados pelo otimizador LLVM no exemplo acima, convém evitá-lo explicitamente.

Se você estiver preocupado com esse custo de cálculo de valor, poderá criá-lo uma vez e fazer o cache assim.

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

Ou assim, se você deseja ocultar completamente a existência do cache.

static var value: Int {
    struct Local {
        static let cache = getTheNumber()
    }
    return Local.cache
}
eonil
fonte
5
Isso produz uma propriedade computada, que será recalculada a cada acesso. Nesse caso, pode não ser muito importante, mas acho que vale a pena mencionar para que ninguém use essa solução alternativa para objetos maiores.
Nick Podratz 25/10/2015
@NickPodratz também seria uma propriedade calculada? private static let _value: Int = 111 static var value: Int { return _value }ele não tem o get {mas o compilador menciona algo sobre propriedade computada se eu usar varem vez delet
hashier
1
@hashier é. Dentro das chaves, você cria um fechamento, o que getestá implícito neste caso. O que você pode fazer é em vez de atribuir o resultado do fechamento para a variável de modo que o encerramento é chamado apenas uma vez: let value: Int = { return 111 }(). Os colchetes no final chamam de fechamento. Mas lembre-se de que essa é uma propriedade armazenada novamente e, portanto, não está disponível em extensões.
Nick Podratz
Concorde com a avaliação de @NickPodratz. Embora isso resolva o erro mencionado pelo OP e, portanto, faça disso uma resposta legítima, ele não oferece nenhum benefício se você deseja que sua variável seja realmente estática (o que parece ser o ponto). A resposta de Alex é melhor nesse caso (assumindo pura Swift)
Matt Long
18

Eu também tive esse erro.

Meu problema foi apenas uma var estática em uma extensão rápida.

extension NotificationsViewController: UITableViewDataSource , UITableViewDelegate {

    static var timeIntervalFormatter = NSDateComponentsFormatter()

}

Mover para a implementação da classe resolveu o problema para mim.

JulianM
fonte
7

Acabei de me deparar com o mesmo problema com uma causa diferente e gostaria de publicá-lo aqui para outras pessoas com a mesma mensagem de erro inútil.

Uma classe final que substitui uma variável computada definida em uma extensão também causa esse erro. Ele funciona para funções e, portanto, parece um bug do 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
fonte
7

Resolvi esse problema movendo a declaração estática para a nova estrutura que defini na extensão.

Então, em vez disso:

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

Eu tenho isto:

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

Você pode marcá-lo como privado para evitar esse erro. Se você deseja expô-lo, pode envolvê-lo em uma função pública:

extension AAA {

    private static let value = 111

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

No meu caso, apenas referenciei a propriedade na própria extensão, portanto, não houve necessidade de expô-la.

pulse4life
fonte