Swift中的延迟加载是一种将对象的创建或其它高负荷操作延迟到真正需要时才执行的机制。当为iOS进行编程时,仅在需要时才分配内存,对小内存的手机而言(相对于电脑)非常实用。
Objective-C
中的延迟加载在Objecitve-C中,如果你要延迟加载一个可变数组,你需要这么做:
@property (nonatomic, strong) NSMutableArray *players;
- (NSMutableArray *)players {
if (!_players) {
_players = [[NSMutableArray alloc] init];
}
return _players;
}
Objective-C初学者要理解这个机制首先要知道getter方法名必须跟属性名一致。如果你拼写错了,这个机制会失效,每次访问players
会返回nil
。
他们还需要知道_players
实例变量是在属性同步时默认创建的,需要在属性名前加上下划线,才能访问到这个实例变量。
这些对初学者来说都不那么直观。
Swift
方式的延迟加载而在Swift中,你只需一行代码即可实现此机制:
lazy var players = String[]()
简单、简洁,直入主题。
但你得记住,你必须使用var
关键字来定义延迟加载的属性,不能使用let
关键字,因为常量必须在实例构建时赋值。
如果你想给延迟加载加上一些逻辑处理,Swift允许你在属性后面定义一个闭包调用(闭包的返回值会作为属性的默认值):
(1)
lazy var text:UITextField = {
var text = UITextField()
text = UITextField(frame: CGRectMake(100, 100, 100, 100))
text.backgroundColor = UIColor.redColor()
text.placeholder = "姓名"
text.delegate = self
return text
}()
如果你愿意,你也可以使用实例方法来初始化延迟加载属性:
(2)
lazy var players: String[] = self.initialPlayers()
func initialPlayers() -> String[] {
var players = ["Mike Buss"]
return players
}
或者用个类方法也可以:
(3)
lazy var players = MultipeerManager.initialPlayers()
class func initialPlayers() -> String[] {
var players = ["Mike Buss"]
return players
}
但大家现在更倾向于使用新的闭包语法,因为它将逻辑代码就定义在了属性声明的旁边。
一种使用场景是,一个对象的属性的初始值依赖与其它的属性,所以必须先创建出这个对象,才能知道这个属性的值。
举例来说,你有一个Person
类以及一个personalizedGreeting
属性。这个personalizedGreeting
属性需要在对象创建完成后才延迟加载,因为只有在对象创建完成后它才能知道问候的人是谁(person的name
)。请看代码:
class Person {
var name: String
lazy var personalizedGreeting: String = {
[unowned self] in
return "Hello, \(self.name)!"
}()
init(name: String) {
self.name = name
}
}
注意,你必须使用
[unowned self]
来避免循环引用。[unowned self]定义了一个在闭包中需要使用的、存在于闭包外的属性/变量列表,又叫捕获列表(capture list
)。
当你实例化一个person
时,他的问候语greeting
此时并没有创建:
let person = Person(name: "Robert Redford”)
// person.personalizedGreeting is nil
但是当你尝试打印出问候语时,这句问候语会自动生成出来:
NSLog(person.personalizedGreeting)
// personalizedGreeting is calculated when used
// and now contains the value "Hello, Robert Redford!"
另一种适合延迟加载的场景,是在属性的初始值需要进行大量计算之时。
举例来说,当你有个对象需要执行一个高负荷的算法来确定一张图片中的人脸个数,你可以将numberOfFaces
属性设置为延迟加载。
或者当你有个类需要计算多个大数的值,你希望它们能在需要的时候才被计算出来:
class MathHelper {
lazy var pi: Double = {
// Calculate pi to an insane number of digits
return resultOfCalculation
}()
}