懒惰加载属性在swift中

时间:2014-06-05 19:38:38

标签: swift

我试图围绕Swift语言。使用Objective-C在代码中构建视图时的一个常见模式是覆盖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的配置在其设置中自包含,但不会导致每次重新配置它们。

我们似乎无法访问Swift中的后备存储,而@lazy关键字实际上并没有相同的语义。

我很好奇是否有人在Swift中发现了类似的模式,允许人们以一种整洁的语法方式将变量和常量的配置与声明保持在一起,每次都不会导致重新配置?

7 个答案:

答案 0 :(得分:71)

我认为使用闭包初始化的lazy属性可以起作用:

lazy var myLabel: UILabel = {
    var temporaryLabel: UILabel = UILabel()
    ...
    return temporaryLabel
}()

当我读到“The Swift Programming Language。”(Checkerboard示例)时,闭包仅被评估一次。)

答案 1 :(得分:6)

class Thingy {
    init(){
        println("making a new Thingy")
    }
}

var thingy = {
    Thingy();
}()

println("\(self.thingy)")
println("\(self.thingy)")

日志消息"制作一个新的Thingy"只出现一次,证明只创造了一个Thingy - 封闭只被调用一次,即初始化这个属性。这实际上就是你所描述的。您所要做的就是向闭包添加更多内容,以便为返回的对象配置它。

如果你创建了var @lazy并注释掉了println语句,那么没有创建了Thingy,证明懒惰符合它的意图;但是,你可以省略这一点,因为你知道标签实际上总是在早期需要。 @lazy的要点是阻止永远的关闭被调用,除非调用getter,但是你总是会调用getter,这样在你的情况下毫无意义。

答案 2 :(得分:4)

这几乎是Swift版本的Objective C示例。 (简化为使用Int而不是视图)

class Foo {
    var _value: Int?
    var value: Int {
        get {
            if !_value {
                _value = 123
            }
            return _value!
        }
    }
}

Foo().value //-> 123

虽然它不是很漂亮。

答案 3 :(得分:4)

Apple似乎采取了不同的做法......如果我在Xcode中创建一个新项目并添加Core Data,那么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

尽管如此,这是因为变量是在初始化时创建的,但只是随后阅读而@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 points out on Twitter

  

@runmad你的方法保留的一件事是只读   财产状况。 @lazy不能和let一起使用,这是一个   问题。

答案 4 :(得分:3)

正如ChristianOtkjær的回答一样,也可以为@lazy var分配一个类方法:

class MyClass {

  @lazy var myLabel : UILabel = MyClass.newLabel()

  class func newLabel() -> UILabel {
    var temporaryLabel : UILabel = UILabel()
    ...
    return temporaryLabel
  }
}

它实际上与使用闭包相同,但是如果闭包中有太多代码行,则可以在声明所有属性和初始化方法之后将该代码放在其他地方的类方法中。

答案 5 :(得分:1)

您可以在@lazy变量上提供一个闭包来声明它应该如何创建:

class Blah {
    @lazy var label: () -> UILabel = {
        var view:UILabel = UILabel();
        //Do stuff here
        return view;
    }
}

答案 6 :(得分:0)

Swift 3.0

我更喜欢这种内联风格。

lazy var myLabel: UILabel = self.createMyLabel()

private func createMyLabel() -> UILabel {
    let mylabel = UILabel()
    // ...
    return mylabel
}