【发布时间】:2017-03-01 05:27:54
【问题描述】:
我对在哪里使用 lazy 功能感到困惑,我的意思是说我应该在集合中使用 lazy 关键字的哪种类型的条件。
【问题讨论】:
-
接受的答案与
NSMutableDictionary.lazy无关……属性的lazy关键字完全不同。
标签: objective-c swift nsmutabledictionary lazy-sequences
我对在哪里使用 lazy 功能感到困惑,我的意思是说我应该在集合中使用 lazy 关键字的哪种类型的条件。
【问题讨论】:
NSMutableDictionary.lazy无关……属性的lazy关键字完全不同。
标签: objective-c swift nsmutabledictionary lazy-sequences
来自 Apple 文档:
惰性存储属性是在第一次使用时才计算初始值的属性。您可以通过在声明之前编写惰性修饰符来指示惰性存储属性。
使用@lazy属性时会记住以下内容:
var 关键字声明,而不是使用let 常量。 我们如何在 Objective-C 中实现惰性功能
@property (nonatomic, strong) NSMutableArray *players;
- (NSMutableArray *)players
{
if (!_players) {
_players = [[NSMutableArray alloc] init];
}
return _players;
}
现在在 Swift 中,您可以使用 lazy 属性实现相同的功能。见下例
class Person {
var name: String
lazy var personalizedGreeting: String = {
return "Hello, \(self.name)!"
}()
init(name: String) {
self.name = name
}
}
现在,当您初始化一个人时,他们的个人问候语尚未创建:
let person = Person(name: "John Doe") // person.personalizedGreeting is nil
但是,当您尝试打印个性化问候语时,它是即时计算的:
print(person.personalizedGreeting)
// personalizedGreeting is calculated when used
// and now contains the value "Hello, John Doe!"
我希望这能帮助你理解惰性属性的功能。
【讨论】:
NSMutableDictionary.lazy无关。
惰性求值是将表达式的求值推迟到需要结果时。这与 eager 评估 不同,后者是立即完成表达式的评估。
考虑这个表达式:
let input = [1, 2, 3]
let evens = input.map{ $0 * 2 }
每个数字 (1, 2, 3) 通过闭包{ $0 * 2 } 映射到一个新值,闭包将它们乘以 2。这个评估是急切地完成的。也就是说,执行此行的时刻是在执行map 函数的评估时,计算结果存储在evens 中。 input 是 Array<Int> 类型,结果 evens 也是 Array<Int> 类型。
现在考虑这个表达式:
let input = [1, 2, 3]
let evens = input.lazy.map{ $0 * 2 }
每个数字 (1, 2, 3) 将通过闭包 { $0 * 2 } 将它们乘以 2 为一个新值。但是,此评估是延迟完成的。也就是说,在执行这条线的那一刻,乘法还没有完成。相反,闭包{ $0 * 2 } 被存储以供将来参考。 input 是 Array<Int> 类型,结果 evens 也是 LazyMapRandomAccessCollection<Array<Int>, Int> 类型。乘法被推迟到元素被访问。如果一个元素从未被访问过,那么它就永远不会被处理。
在这种微不足道的情况下,存储闭包以供将来评估的簿记开销将比仅仅急切地计算结果要大。但是,您可以设想这样的情况:
let input = 1...1000000000
let evens = input.lazy.map{ $0 * 2 }
print(evens[0])
在序列中的所有1000000000 中,只有一个被使用过。评估闭包 1000000000 次,以产生 1000000000 结果,将它们全部存储在内存中确实效率低下,如果只需要第一个元素的话。
lazy 是Sequence 协议的实例方法。所有符合要求的类型,包括NSMutableDictionary 都实现了它。他们都做同样的事情:他们推迟处理map 和filter 语句中的元素,直到需要他们的结果。在元素很多且只需要其中一小部分的情况下,这可以节省内存和处理时间。
【讨论】:
lazy 的工作方式是初始化程序(或 init 方法)仅在第一次访问变量或属性时运行。我看到它在您的代码中不起作用(至少立即)的一个主要原因,那是因为您将两个惰性实例化代码打包到一个方法中(loadEntriesIfNeeded)。
要使用惰性实例化,您可能需要扩展 NSMutableArray 和 NSDictionary 并为您的惰性实例化覆盖或创建自定义初始化程序。然后,将 loadEntriesIfNeeded 中的代码分发到各自的初始化程序中。
import Swift
println("begin")
class ClassWithLazyProperties {
lazy var entries:[String] = ClassWithLazyProperties.loadStuff()
lazy var entriesByNumber:Dictionary<Int, String> = {
var d = Dictionary<Int, String>()
for i in 0..<self.entries.count {
d[i] = self.entries[i]
}
return d
}()
private class func loadStuff() -> [String] {
return ["Acai", "Apples", "Apricots", "Avocado", "Ackee", "Bananas", "Bilberries"]
}
}
let c = ClassWithLazyProperties()
c.entriesByNumber
// 0: "Acai", 1: "Apples", 2: "Apricots", 3: "Avocado", 4: "Ackee", 5: "Bananas", 6: "Bilberries"]
println("end")
【讨论】:
lazy vars无关。此外,您应该避免写 for i in 0..<self.entries.count 之类的东西来支持 for i in self.entries.indices。此外,如果您需要数组的索引和元素,则应使用for (index, element) in self.entries.enumerated()