Объявление не может быть одновременно "окончательной" и "динамической" ошибкой в ​​Swift 1.2.

123

Декларация valueниже

import Foundation

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

вызывает следующую ошибку компиляции

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

Почему это происходит и как с этим бороться?

Я использую Swift 1.2 (версия из Xcode 6.3.1 6D1002)

eonil
источник
func test2Декларации не требуется , чтобы вызвать ошибку, так как из Xcode 7.3.1.
Роб Майофф
1
Swift bug SR-993
роб майофф
Просто поместите эту статическую переменную в другую лучшую структуру именования
onmyway133

Ответы:

224

Эта проблема возникает из-за того, что Swift пытается создать динамический аксессор для статического свойства для совместимости с Obj-C, поскольку класс наследуется от NSObject.

Если ваш проект находится только в Swift, вместо использования средства varдоступа вы можете избежать проблемы с помощью @nonobjcатрибута в Swift 2.0:

import Foundation

class AAA: NSObject {}
extension AAA {
    @nonobjc static let value = 111
}
Алекс Претцлав
источник
В моем проекте есть файлы Objective-C, но ни один из этих кодов не взаимодействует с экземплярами этого класса ( AAAздесь), так что я думаю, что я не уверен?
Николас Миари
Это должен быть выбранный ответ при использовании чистой кодовой базы Swift.
idzski
Я пытался добавить статические (классовые) переменные в NSManagedObjectподкласс. Это исправлено!
Николас Миари
Я единственный, кто нашел это исправление, чтобы полностью испортить SourceKitService для Xcode 7.3?
NoodleOfDeath
57

Вы получите эту ошибку, если ваш класс удовлетворяет этим условиям.

  • Подкласс от NSObject.
  • Есть static letполе.
  • Получает доступ к полю из метода экземпляра через dynamicType.

Я не знаю, почему это происходит, но вы можете попробовать этот обходной путь.

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

Или в более короткой форме.

static var value: Int {
    return 111
}

Используйте static var { get }вместо static let.


Хотя средство получения свойства и стоимость его вызова с большой вероятностью будут исключены оптимизатором LLVM в приведенном выше примере, вы можете явно избежать этого.

Если вас беспокоит такая стоимость расчета стоимости, вы можете создать ее один раз и кэшировать вот так.

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

Или вот так, если вы хотите полностью скрыть существование кеша.

static var value: Int {
    struct Local {
        static let cache = getTheNumber()
    }
    return Local.cache
}
eonil
источник
5
Это создает вычисляемое свойство, которое будет пересчитываться при каждом доступе. В этом случае это может не иметь большого значения, но я думаю, что об этом стоит упомянуть, чтобы никто не использовал этот обходной путь для больших объектов.
Ник Подрац
@NickPodratz, это тоже будет расчетное свойство? private static let _value: Int = 111 static var value: Int { return _value }у него нет, get {но компилятор упоминает что-то о вычисляемом свойстве, если я использую varвместоlet
hashier
1
@hashier это так. Внутри фигурных скобок вы создаете замыкание, getв данном случае неявно. Что вы можете сделать вместо этого правопреемником результат закрытия переменной так , что замыкание называется только один раз: let value: Int = { return 111 }(). Скобки в конце называют закрытие. Но имейте в виду, что это снова сохраняемое свойство и поэтому не доступно в расширениях.
Ник Подрац
Согласитесь с оценкой @NickPodratz. Хотя это устраняет ошибку, о которой упоминает OP, и, следовательно, делает это законным ответом, это не дает никаких преимуществ, если вы хотите, чтобы ваша переменная действительно была статической (что похоже на суть). Ответ Алекса в этом случае лучше (при условии чистого Swift)
Мэтт Лонг
18

У меня тоже была эта ошибка.

Моя проблема заключалась в статическом var в быстром расширении.

extension NotificationsViewController: UITableViewDataSource , UITableViewDelegate {

    static var timeIntervalFormatter = NSDateComponentsFormatter()

}

Перенос его в реализацию класса решил проблему для меня.

JulianM
источник
7

Я только что наткнулся на ту же проблему с другой причиной и хотел бы опубликовать ее здесь для других людей, которые сталкиваются с тем же бесполезным сообщением об ошибке.

Последний класс, который переопределяет вычисляемую переменную, определенную в расширении, также вызывает эту ошибку. Однако он работает для функций и поэтому выглядит как ошибка компилятора.

// 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
источник
7

Я решил эту проблему, переместив статическое объявление в новую структуру, которую я определил в расширении.

Итак, вместо этого:

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

У меня есть это:

extension NSOperationQueue {        
    struct Shared {
        static var parsingQueue : NSOperationQueue = {
            let queue = NSOperationQueue()
            queue.maxConcurrentOperationCount = 1
            return queue                
            }()
    }
}
VojtaStavik
источник
0

Вы можете пометить его как частный, чтобы предотвратить эту ошибку. Если вы хотите раскрыть его, вы можете заключить его в публичную функцию:

extension AAA {

    private static let value = 111

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

В моем случае я ссылался на свойство только в самом расширении, поэтому не было необходимости раскрывать его.

pulse4life
источник