Инициализация инициализации Lazy в Swift

Как реализовать следующий шаблон в Swift?

Класс Container инициализируется массивом JSON, содержащим словари. Эти словари используются для инициализации классов Entry. Однако инициализация объектов Entry происходит лениво, когда доступно свойство entries или searchEntries.

@interface Container

@property (readonly, nonatomic) NSArray *entryDicts;

@property (readonly, nonatomic) NSArray* entries;
@property (readonly, nonatomic) NSDictionary *searchEntries;

@end



@implementation Container

- (instancetype)initWithArray:(NSArray *)array
{
    self = [super init];
    if (self) {
        _entryDicts = array;
    }
    return self;
}

@synthesize entries = _entries;
- (NSArray *)entries
{
    [self loadEntriesIfNeeded];
    return _entries;
}

@synthesize entriesByNumber = _entriesByNumber;
- (NSDictionary *)entriesByNumber
{
    [self loadEntriesIfNeeded];
    return _entriesByNumber;
}

- (void)loadEntriesIfNeeded
{
    if (_entries == nil) {
        // Load entries
        NSMutableArray *entries = [NSMutableArray arrayWithCapacity:[self.entriesDict count]];
        NSMutableDictionary *entriesByNumber = [NSMutableDictionary dictionaryWithCapacity:[self.entriesDict count]];

        [self.entriesDict enumerateKeysAndObjectsUsingBlock:^(NSString *number, NSDictionary *entryDict, BOOL *stop) {
            Entry *entry = [[Entry alloc] initWithDictionary:entryDict container:self];
            [entries addObject:entry];
            entriesByNumber[number] = entry;
        }];

        _entries = [entries copy];
        _entriesByNumber = [entriesByNumber copy];

        // Delete dictionaries
        _entriesDict = nil;
    }
}

@end

Ответ 1

Кажется, что на этот вопрос в основном ответили, но чтобы вернуться к исходному сообщению, вот (IMHO) относительно короткий текст в Swift. Ключ в том, что вы можете связывать ленивые свойства. Обратите внимание, что я использовал как функцию класса, так и замыкание - тоже нормально.

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")

Ответ 2

Как насчет этого:

class Container {

    lazy var entries: [String] = self.newEntries()

    func newEntries() -> [String] {

        // calculate and return entries

    }

}

Ответ 3

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

class Lazy {
    var lazyVariable:String?

    func lazilyGetEntries() -> String {
        if let possibleVariable = self.lazyVariable { // optional already exists
            return possibleVariable
        }
        else {                                        // optional does not exist, create it
            self.lazyVariable = String()
            return self.lazyVariable!
        }
    }
}

Ответ 4

Лестями работы является то, что инициализатор (или метод init) запускается только при первом доступе к переменной или свойству. Я вижу одну основную причину, по которой она не будет работать (по крайней мере, сразу) в вашем коде, и это связано с тем, что вы упаковали два ленивых кода-экземпляра в один метод (loadEntriesIfNeeded).

Чтобы использовать ленивую инстанцирование, вам может потребоваться расширить NSMutableArray и NSDictionary и переопределить или создать пользовательский инициализатор для вашей ленивой копии. Затем распределите код внутри loadEntriesIfNeeded в соответствующие инициализаторы.

В инициализаторе записей:

NSMutableArray *entries = [NSMutableArray arrayWithCapacity:[self.entriesDict count]];
[self.entriesDict enumerateKeysAndObjectsUsingBlock:^(NSString *number, NSDictionary *entryDict, BOOL *stop) {
            Entry *entry = [[Entry alloc] initWithDictionary:entryDict container:self];
            [entries addObject:entry];}];
_entries = [entries copy];

Затем в entryByNumber инициализатор:

NSMutableDictionary *entriesByNumber = [NSMutableDictionary dictionaryWithCapacity:[self.entriesDict count]];
// Then do fast enumeration accessing self.entries to assign values to entriesByNumber.
// If self.entries is null, the lazy instantiation should kick in, calling the above code
// and populating the entries variable.
_entriesByNumber = [entriesByNumber copy];

Затем вы можете создать свои ленивые переменные, вызвав пользовательские инициализаторы.

@lazy var entries: CustomArray = custominitforarray()
@lazy var entriesByNumber: CustomDictionary = custominitfordictionary()

PS: Почему у вас нет свойства для entryByNumber? Я предполагаю, что это личное? Пожалуйста, проверьте это и ответьте на результат, поскольку я слишком ленив, чтобы сделать это сам.

Ответ 5

Вы можете использовать Lazy Stored Properties в Swift для реализации шаблона Lazy Instantiation. Это делается добавлением атрибута @lazy перед объявлением сохраненного свойства.

Следует иметь в виду две вещи:

  • Lazy свойства должны быть инициализированы при объявлении
  • Lazy свойства могут использоваться только для членов структуры или класса (следовательно, почему нам нужно использовать DataManager)

Здесь некоторый код, который вы можете вывести на игровой площадке, чтобы увидеть, как работает атрибут @lazy

// initialize your lazily instantiated data
func initLazyData() -> String[] {
    return ["lazy data"]
}

// a class to manage the lazy data (along with any other data you want)
class DataManager {
    @lazy var lazyData = initLazyData()

    var otherData = "Other data"
}

// when we create this object, the "lazy data" array is not initialized
let manager = DataManager()

// even if we access another property, the "lazy data" array stays nil
manager.otherData += ", more data"
manager

// as soon as we access the "lazy data" array, it gets created
manager.lazyData
manager

Для получения дополнительной информации вы можете найти раздел Lazy Stored Properties на странице свойств руководства по языку Swift Programming Language Guide. Обратите внимание, что эта ссылка предназначена для предварительной публикации документации.

Ответ 6

Вы указываете ленивое сохраненное свойство, записывая атрибут @lazy перед его объявлением. "

@lazy var lazyVariable:String? = ""

Пожалуйста, имейте в виду, что у ленивого свойства должен быть инициализатор.

Ответ 7

В Swift есть атрибут @lazy. Я нашел небольшой пост здесь, и я рекомендую посмотреть три видео Swift от Apple здесь (Введение в Swift, Intermediate Swift, Advanced Swift). Эти видеоролики показывают много вещей, а продвинутый - продвинутый...

Ответ 8

Я нашел это в PageBaseApplication

var modelController: ModelController {
    // Return the model controller object, creating it if necessary.
    // In more complex implementations, the model controller may be passed to the view controller.
    if !_modelController {
        _modelController = ModelController()
    }
    return _modelController!
}

var _modelController: ModelController? = nil

похож на то, что упоминал @Brian Tracy, но вместо uscing вместо функции func

Ответ 9

Так как свойство entries является всего лишь массивом значений в entriesByNumber, вы можете выполнять всю загрузку только в entriesByNumber и просто иметь entries зависеть от entriesByNumber

lazy var entriesByNumber: [String : Entry] = {
        var ret: [String : Entry] = [:] 
        for (number, entryDict) in entriesDict
        {
            ret[number] = Entry(dictionary: entryDict, container: self)
        }
        return ret
    }

var entries: [Entry] 
{
    get { return self.entriesByNumber.values }
}