2015-04-23 4 views
120

La dichiarazione di value sottoUna dichiarazione non può essere contemporaneamente ed errore 'finale' 'dinamica' a Swift 1.2

import Foundation 

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

causa il seguente errore di compilazione

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

Perché questo accade, e come posso affrontare questo?

Sto usando Swift 1.2 (la versione fornita all'interno di Xcode 6.3.1 6D1002)

+0

La dichiarazione 'func test2' non è necessario per innescare l'errore, come di Xcode 7.3.1. –

+1

[Swift bug SR-993] (https://bugs.swift.org/browse/SR-993) –

+0

Basta inserire la variabile statica in un'altra struttura di denominazione migliore – onmyway133

risposta

222

Questo problema si pone perché Swift sta cercando di generare una funzione di accesso dinamico per la proprietà statica per la compatibilità Obj-C, dal momento che la classe eredita da NSObject.

Se il progetto è a Swift solo, piuttosto che utilizzare una funzione di accesso var si può evitare il problema tramite l'attributo @nonobjc a Swift 2.0:

import Foundation 

class AAA: NSObject {} 
extension AAA { 
    @nonobjc static let value = 111 
} 
+0

Il mio progetto ha alcuni file Objective-C, ma nessuno di quel codice interagisce con le istanze di questa classe ('AAA' qui), quindi suppongo di essere in chiaro? –

+0

Questa dovrebbe essere la risposta selezionata se si utilizza una base di codice Swift pura. – Skyboat

+0

Stavo cercando di aggiungere vars statiche (di classe) a una sottoclasse 'NSManagedObject'. Questo l'ha risolto! –

57

Otterrete questo errore se la classe soddisfa queste condizioni.

  • Sottoclasse da NSObject.
  • Ha un campo static let.
  • Accede al campo da un metodo di istanza tramite dynamicType.

Non so perché questo accada, ma puoi provare questa soluzione alternativa.

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

O in forma più breve.

static var value: Int { 
    return 111 
} 

Uso static var { get } invece di static let.


Anche se la chiusura di richiamo di proprietà e il suo costo di chiamata è molto probabile che sia eliminato da LLVM ottimizzatore nell'esempio di cui sopra, si potrebbe desiderare di evitarlo in modo esplicito.

Se si è preoccupati di tale costo di calcolo del valore, è possibile crearlo una volta e memorizzare nella cache in questo modo.

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

O come questo se si desidera nascondere completamente il cache.

static var value: Int { 
    struct Local { 
     static let cache = getTheNumber() 
    } 
    return Local.cache 
} 
+5

Questo produce una proprietà calcolata, che verrà ricalcolata ad ogni accesso . In questo caso potrebbe non essere troppo importante, ma penso che sia degno di nota in modo che nessuno usi questa soluzione alternativa per gli oggetti più grandi. –

+0

@NickPodratz sarebbe anche una proprietà calcolata? 'statico privato let _value: Int = 111'' valore var statico: Int {return _value} 'non ha il' get {'ma il compilatore menziona qualcosa sulla proprietà calcolata se uso' var' invece di 'let ' – hashier

+1

@hashier è. All'interno delle parentesi graffe si crea una chiusura, in questo caso il 'get' è implicito. Quello che puoi fare invece è assegnare il risultato della chiusura alla variabile in modo che la chiusura venga chiamata una sola volta: 'let valore: Int = {return 111}()'. Le parentesi alla fine chiamano la chiusura. Ma ricorda che questa è una proprietà memorizzata e quindi non disponibile nelle estensioni. –

7

Ho appena inciampato sopra lo stesso problema con una causa diversa e vorrei postare qui per le altre persone che vivono lo stesso messaggio di errore inutile.

Una classe finale che sovrascrive una variabile calcolata definita in un'estensione causa anche questo errore. Funziona comunque per funzioni e quindi sembra un bug del compilatore.

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

Ho avuto anche questo errore.

Il mio problema era solo un statico in un'estensione rapida.

extension NotificationsViewController: UITableViewDataSource , UITableViewDelegate { 

    static var timeIntervalFormatter = NSDateComponentsFormatter() 

} 

Spostarlo nell'implementazione di classe ha risolto il problema per me.

7

Ho risolto questo problema spostando la dichiarazione statica nella nuova struttura definita nell'estensione.

Così, invece di questo:

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

ho questo:

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

è possibile contrassegnare come privata per evitare questo errore. Se si desidera esporre esso, si può avvolgere in una funzione pubblica:

extension AAA { 

    private static let value = 111 

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

Nel mio caso, ho fatto riferimento solo la proprietà nell'estensione per sé, quindi non c'era bisogno di esporlo.

0

Come un leggero miglioramento rispetto @Eonil's answer, il get non è necessario:

static var value: Int { return 111 }