#swift #performance #function #static #optional
#swift #Производительность #функция #статическое #тип параметра
Вопрос:
Я разрабатываю класс с большим функционалом. Некоторым функциям требуется много времени, поэтому я бы сохранил результат при создании для последующего доступа. Но если это значение никогда не требовалось, вычисление было потрачено впустую.
В этом случае я бы использовал маркер, если значение уже вычислено. Если да, используйте сохраненное значение. Если нет, он вычислит значение и сохранит его в своей статической переменной.
Итак, мне нужна сама функция, маркер и переменная для сохраненного результата. Три элемента для каждой функции! Есть ли в Swift что-нибудь встроенное вроде «необязательных статических функций»??
Я бы предпочел использовать
a = function()
для каждого вызова. Первый вызов вычислит и сохранит результат, все остальные получат только предварительно вычисленный результат (например, из кэша).
Ответ №1:
Отложенные сохраненные свойства
В своем комментарии ниже вы описываете, что
Но я не имел в виду глобальные статические значения, а для каждого экземпляра. Итак, если вы используете
foo1
andfoo2
, thanfoo2
должен вычислять свою собственную функцию для своих собственных, потому что это зависит только от значенийfoo2
. Он не может полагаться на глобально вычисляемую версию, которая была выполнена ранее с помощьюfoo1
. Итак, это что-то вроде «локальной статической переменной»
Похоже, у вас могут быть сложные вычисления для каждого экземпляра Foo
, и вы хотите убедиться, что это вычисление выполняется не более одного раза для каждого данного экземпляра Foo
. Это требование очень близко к самому определению отложенных (хранимых) свойств в Swift: если выполняется первый вызов для получения значения данного отложенного свойства, будет создан экземпляр свойства (например, путем вызова некоторого метода, выполнения закрытия, выполняемого только один раз, или просто с помощью заданного значения / литерала), а значение сохранено в свойстве. Все последующие вызовы отложенного свойства будут просто использовать сохраненное значение (при условии, что вы не решите изменять свойство: свойства отложенного экземпляра могут не быть неизменяемыми).
Например, на практике:
class Foo {
lazy var bar: Int = self.reallyHeavyCalculation()
lazy var baz: Int = {
print("... heavy stuff")
return 2 * self.baq
}()
private func reallyHeavyCalculation() -> Int {
// ...
print("... other heavy stuff")
return 42
}
var bax: Int?
var baq: Int // instance value used in once-only computation
// of (lazy) property baz: at baz instantiation
init(baq: Int) { self.baq = baq }
}
let foo1 = Foo(baq: 50)
print("My first foo has never accessed his bar or baz")
foo1.bax = foo1.bar // at first call: compute 'bar' for this Foo instance
var baxBaz = foo1.bar // 'foo1.bar' already computed
print(foo1.baz) // at first call: compute 'baz' for this Foo instance
baxBaz = foo1.baz // 'foo1.baz' already computed
/* Prints:
My first foo has never accessed his bar or baz
... other heavy stuff
... heavy stuff
100 */
Статические свойства всегда вычисляются лениво
(Когда я изначально прочитал ваш вопрос, я понял, что ваш вариант использования относится к классу / статическим свойствам, которые должны быть вычислены только один раз. Я оставлю эту часть ответа, поскольку она по-прежнему актуальна для темы и может быть полезна будущим читателям)
Статические свойства всегда вычисляются лениво, что означает, что они будут созданы только со значением, заданным для них по крайней мере при одном вызове. После создания этого экземпляра, например, в случае статического неизменяемого свойства, вычисленное только один раз значение становится легко доступным и сохраняется в статическом свойстве.
Мы можем прочитать из руководства по языку — Свойства:
Свойства типа
…
Сохраненные свойства типа лениво инициализируются при первом обращении к ним. Они гарантированно инициализируются только один раз, даже при одновременном доступе к нескольким потокам, и их не нужно помечать
lazy
модификатором.…
Глобальные и локальные переменные
…
Глобальные константы и переменные всегда вычисляются лениво, аналогично отложенным сохраненным свойствам. В отличие от отложенных сохраненных свойств, глобальные константы и переменные не обязательно помечать
lazy
модификатором.
Мы можем проверить это поведение на простом примере:
class Foo {
static let foo: Int = reallyHeavyCalculation() // lazy
static let bar: Int = {
print("... heavy stuff")
return 99
}() // lazy
private static func reallyHeavyCalculation() -> Int {
// ...
print("... other heavy stuff")
return 42
}
var bax: Int? = nil
var baz = Foo.bar
}
print("I've never had a foo")
let foo1 = Foo()
// first initializion of instance member 'baz' by
// type member 'bar': compute bar
print("I have a foo")
foo1.bax = Foo.foo // at first call: compute 'Foo.foo'
let foo2 = Foo() // 'Foo.bar' already computed
print("I have another foo")
foo2.bax = Foo.foo // 'Foo'foo' already computed
/* Prints:
I've never had a foo
... heavy stuff
I have a foo
... other heavy stuff
I have another foo */
В приведенном выше примере метод reallyHeavyComputation()
(/closure, связанный с Foo.bar
) будет вызван ровно один раз на случай, если вы запросите (хотя бы один раз) значение статического неизменяемого свойства Foo.foo
(/ Foo.bar
). Никаких дополнительных вызовов reallyHeavyComputation()
(/closure) сделано не будет, даже если вы повторно запрашиваете значение Foo.foo
(/ Foo.bar
).
Комментарии:
1. Спасибо dfri. Но я не имел в виду глобальные статические значения, а для каждого экземпляра. Итак, если вы используете foo1 и foo2, то foo2 должен вычислять свою собственную функцию для своей собственной, потому что это зависит только от значений foo2. Он не может полагаться на глобально вычисляемую версию, которая была выполнена ранее с помощью foo1. Итак, это что-то вроде «локальной статической переменной» 🙂 . Это трудно описать.
2. @PeterSilie А, понятно. Возможно, вам следует указать в своем вопросе, что вы конкретно ссылаетесь на методы экземпляра . Я мог бы изменить форму / добавить к своему ответу в свете ваших новых деталей. Но, похоже, что в этом случае вы просто ищете сохраненные
lazy
свойства.3. @PeterSilie обновил мой ответ разделом о сохраненных ленивых свойствах (экземпляра). Обратите внимание, однако, что статические свойства уникальны для типа , а не для его экземпляров, поэтому я не уверен, что вы имеете в виду, когда пишете «Если нет, он вычислит значение и сохранит его в своей статической переменной». . С этим описанием экземпляры вашего класса будут выполнять вычисления только один раз, а затем использовать их результат для многократной перезаписи статического свойства, связанного с типом класса.