Ложная загрузка свойств в быстрой

Я пытаюсь обернуть голову вокруг языка Swift. Общий шаблон при построении представлений в коде с Objective-C заключается в переопределении свойств пользовательского интерфейса и ленивой загрузке их так:

@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, который позволяет сохранить конфигурацию переменных и констант вместе с их объявлением аккуратным синтаксическим способом, который не приводит к реконфигурации каждый раз?

Ответ 1

Я думаю, что свойство lazy, инициализированное закрытием, будет работать:

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

Как я читал "Быстрый язык программирования". (Пример шахматной доски) закрытие оценивается только один раз).

Ответ 2

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

var thingy = {
    Thingy();
}()

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

Сообщение журнала "Создание нового Thingy" появляется только один раз, доказывая, что был создан только один Thingy - закрытие было вызвано только один раз, а именно для инициализации этого свойства. Это эффективно то, что вы описываете. Все, что вам нужно сделать, это добавить еще к закрытию, чтобы настроить его на возвращаемый объект.

Если вы создадите var @lazy и закомментируете утверждения println, то Thingy не создается, доказывая, что лень делает то, что он предназначен; вы можете опустить это, однако, поскольку вы знаете, что на самом деле этикетка на самом деле всегда понадобится на ранней стадии. Точка @lazy заключается в том, чтобы не допустить, чтобы замыкание вызывалось, пока не будет вызван геттер, но вы всегда будете называть геттер так, чтобы в вашей ситуации было бессмысленно.

Ответ 3

Это в значительной степени версия Swift как пример ObjectiveC. (упрощено использование Int вместо представления)

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

Foo().value //-> 123

Хотя это не очень красиво.

Ответ 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 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 указывает на Twitter:

@runmad Единственное, что сохраняет ваш подход, - это только чтение состояние имущества. @lazy не может использоваться с let, что является проблема.

Ответ 5

Так же, как вариант ответа Christian Otkjær, также можно назначить метод класса для @lazy var:

class MyClass {

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

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

Это фактически то же самое, что использование замыкания, но в случае, если в замыкании слишком много строк кода, это возможность поместить этот код в метод класса в другое место после объявления всех свойств и методов инициализации.

Ответ 6

Вы можете предоставить закрытие переменной @lazy, чтобы объявить, как она должна быть создана:

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

Ответ 7

Swift 3.0

Я предпочитаю этот встроенный стиль.

lazy var myLabel: UILabel = self.createMyLabel()

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