Подтвердить что ты не робот

Lazy Var vs Let

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

lazy var fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

Дело в том, что после установки fontSize он НИКОГДА не изменится. Поэтому я хотел сделать что-то вроде этого:

lazy let fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

Это невозможно.

Только это работает:

let fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

Итак - я хочу свойство, которое будет лениво загружено, но никогда не изменится. Каков правильный способ сделать это? используя let и забыть о ленивом init? Или я должен использовать lazy var и забыть о постоянном характере свойства?

4b9b3361

Ответ 1

Это последнее из Священных Писаний из примечаний к выпуску Xcode 6.3 Beta/Swift 1.2:

константы были обобщены, чтобы больше не требовать немедленных инициализация. Новое правило состоит в том, что пустая константа должна быть инициализируется перед использованием (например, var) и что это может быть только инициализируется: не инициализируется или не изменяется после инициализации.

Это позволяет использовать такие шаблоны, как:

let x: SomeThing
if condition {
    x = foo()
} else {
    x = bar()
}

use(x)

который раньше требовал использования var, хотя нет мутация происходит. (16181314)

Очевидно, вы были не единственным человеком, разочарованным этим.

Ответ 2

Быстрая книга имеет следующее примечание:

Вы всегда должны объявлять ленивое свойство как переменную (с ключевым словом var), поскольку ее начальное значение может не быть получено до завершения инициализации экземпляра. Постоянные свойства всегда должны иметь значение до завершения инициализации и поэтому не могут быть объявлены ленивыми.

Это имеет смысл в контексте реализации языка, поскольку все константные хранимые свойства вычисляются до завершения инициализации объекта. Это не означает, что семантика let могла быть изменена, когда она используется вместе с lazy, но она не была выполнена, поэтому var остается единственным вариантом с lazy на этом этапе.

Что касается двух вариантов, которые вы представили, я бы решил между ними по эффективности:

  • Если доступ к значению свойства выполняется редко, и дорого вычислять его заранее, я бы использовал var lazy
  • Если значение доступно в более чем 20..30% случаев или оно относительно недорого для вычисления, я бы использовал let

Примечание. Я бы еще больше оптимизировал ваш код, чтобы вставить условное выражение в CGFloat initializer:

let fontSize : CGFloat = CGFloat(someCase  ? 30 : 17)

Ответ 3

Как указывает dasblinkenlight, ленивые свойства всегда должны быть объявлены как переменные в Swift. Однако можно сделать свойство доступным только для чтения, чтобы его можно было только мутировать из исходного файла, в котором был определен Entity. Это самый близкий путь, который я могу определить для определения "ленивого let".

private(set) lazy var fontSize: CGFloat = {
    if someCase {
        return 30
    } else {
        return 17
    }
}()

Ответ 4

Вы можете использовать Burritos для ленивых постоянных свойств. Эта библиотека предоставляет различные оболочки свойств для Swift 5.1. Установите его с CocoaPods, добавив следующую строку в ваш Podfile:

pod 'Burritos'

С помощью этой библиотеки вы можете заменить

lazy var fontSize : CGFloat = {
  if (someCase) {
    return CGFloat(30)
  } else {
    return CGFloat(17)
  }
}()

с

@LazyConstant var fontSize : CGFloat = {
    if (someCase) {
        return CGFloat(30)
    } else {
        return CGFloat(17)
    }
}()

А затем self.fontSize = 20 приводит к ошибке компиляции.