在swift中延迟加载属性
我试图用Swift语言来包装我的头。 在Objective-C的代码中构build视图时的常见模式是重写UI属性,并像这样延迟加载它们:
@property(nonatomic, strong) UILabel *myLabel; - (UILabel *)myLabel { if (!_myLabel) { _myLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0f, 75.0f, 320.0f, 20.0f)]; [_myLabel setFont:[UIFont subHeadlineFont]]; [_myLabel setTextColor:[UIColor subHeadlineColor]]; [_myLabel setText:@"Hello World"]; } return _myLabel; } - (void)viewDidLoad { [super viewDidLoad]; [self.view addSubview:self.myLabel]; }
这允许UIElements的configuration在其设置中自包含,但不会导致每次重新configuration它们。
看来我们没有在Swift中访问后备存储,而@lazy
关键字实际上并没有相同的语义。
我很好奇,如果有人在Swift中发现了一个相似的模式,那么允许将variables和常量的configuration和它们的声明一起以一种整齐的语法方式保存,而不会导致每次重新configuration。
我认为用闭包初始化一个lazy
属性将工作:
lazy var myLabel: UILabel = { var temporaryLabel: UILabel = UILabel() ... return temporaryLabel }()
正如我读“Swift编程语言”(棋盘格示例),封闭仅评估一次)。
class Thingy { init(){ println("making a new Thingy") } } var thingy = { Thingy(); }() println("\(self.thingy)") println("\(self.thingy)")
日志消息“制作一个新的Thingy”只出现一次,certificate只有一个Thingy被创build – 封闭只被调用一次,即初始化这个属性。 这实际上是你所描述的。 所有你需要做的就是添加更多的closures,以configuration它返回的对象。
如果你使用var @lazy
并且注释掉println
语句,那么就不会创buildThingy,这certificate了懒惰是做它打算做的。 你可以省略这个,但是,因为你知道这个标签实际上总是需要提前。 @lazy
是阻止封闭被调用,除非调用getter,但是你总是要调用getter,所以在你的情况下是毫无意义的。
这几乎是你的ObjectiveC例子的Swift版本。 (简化为使用Int
而不是视图)
class Foo { var _value: Int? var value: Int { get { if !_value { _value = 123 } return _value! } } } Foo().value //-> 123
虽然不是很漂亮
苹果似乎做的不同…如果我在Xcode中创build一个新的项目,并添加核心数据,在AppDelegate.swift
有一个例子:
// Returns the managed object model for the application. // If the model doesn't already exist, it is created from the application's model. var managedObjectModel: NSManagedObjectModel { if !_managedObjectModel { let modelURL = NSBundle.mainBundle().URLForResource("MyApp", withExtension: "momd") _managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL) } return _managedObjectModel! } var _managedObjectModel: NSManagedObjectModel? = nil
虽然,这个读给我看,因为variables是在初始化时创build的,不过随后读取和@lazy
看起来像一个更好的实现。 有什么想法吗?
所以我试过这个:
class TestClass { @lazy var myLabel : UILabel = { var temporaryLabel : UILabel = UILabel() return temporaryLabel }() var testLabel: UILabel { if !_testLabel { _testLabel = UILabel() } return _testLabel! } var _testLabel: UILabel? = nil func test () { println(myLabel) println(self.testLabel) } }
而这两者确实只是懒洋洋地创造出来的。 正如@bradlarson 在Twitter上指出的那样 :
@runmad你的方法保留的一件事是属性的只读状态。 让@lazy不能用,这是一个问题。
就像ChristianOtkjær的答案一样,也可以为@lazy var指定一个类方法:
class MyClass { @lazy var myLabel : UILabel = MyClass.newLabel() class func newLabel() -> UILabel { var temporaryLabel : UILabel = UILabel() ... return temporaryLabel } }
这与使用闭包完全相同,但是如果闭包中的代码行数太多,则可以在声明所有属性和初始化方法后,将该代码放在其他位置的类方法中。
您可以在@lazyvariables上提供一个闭包来声明它应该如何创build:
class Blah { @lazy var label: () -> UILabel = { var view:UILabel = UILabel(); //Do stuff here return view; } }
Swift 3.0
我更喜欢这种内联风格。
lazy var myLabel: UILabel = self.createMyLabel() private func createMyLabel() -> UILabel { let mylabel = UILabel() // ... return mylabel }