【问题标题】:Lazy property initialization in SwiftSwift 中的延迟属性初始化
【发布时间】:2014-06-08 15:53:40
【问题描述】:

您将如何在 Swift 中实现以下模式?

Container 类使用包含字典的 JSON 数组进行初始化。这些字典用于初始化Entry 类。但是,Entry 对象的初始化是延迟发生的,当访问 entriessearchEntries 属性时。

@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

【问题讨论】:

    标签: swift


    【解决方案1】:

    这个呢:

    class Container {
    
        lazy var entries: [String] = self.newEntries()
    
        func newEntries() -> [String] {
    
            // calculate and return entries
    
        }
    
    }
    

    【讨论】:

    • 谢谢。类名和函数中的返回值后缺少开头的 {。
    • @Evgeny 修复了丢失的“{”。谢谢!
    【解决方案2】:

    似乎这个问题已经基本得到解答,但回到原来的帖子,这里是(恕我直言)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")
    

    【讨论】:

      【解决方案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】:

        lazy 的工作方式是初始化程序(或 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];
        

        然后在 entriesByNumber 初始化器中:

        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的属性?估计是私信?请测试一下并回复结果,因为我懒得自己做。

        【讨论】:

        • 它变得只是懒惰,在 beta4 中没有“@”
        【解决方案5】:

        您可以在 Swift 中使用延迟存储属性来实现延迟实例化模式。这是通过在存储属性的声明之前添加@lazy 属性来完成的。

        有两点需要牢记:

        • 惰性属性必须在声明时初始化
        • 惰性属性只能用于结构或类的成员(因此我们需要使用 DataManager)

        这里有一些代码可以放入 Playground 以查看 @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
        

        有关更多信息,您可以查看 Swift 编程语言指南的属性页面上的 Lazy Stored Properties 部分。请注意,该链接指向预发布文档。

        【讨论】:

          【解决方案6】:

          您可以通过在声明之前写入@lazy 属性来指示惰性存储属性。”

          @lazy var lazyVariable:String? = ""
          

          请记住,惰性属性必须有一个初始化器。

          【讨论】:

            【解决方案7】:

            Swift 中有一个@lazy 属性。我找到了一个小帖子 here,我建议观看 Apple here 的三个 Swift 视频(Swift 简介、Swift 中级、Swift 高级)。这些视频展示了很多东西,高级的真的很高级......

            【讨论】:

            • 我已经看过所有视频了。不幸的是,@lazy 在这里不起作用。查看我的代码。
            • @Florian 不幸的是,我的开发人员程序尚未激活,所以我无法运行代码:(
            【解决方案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 提到的,但使用变量而不是函数

            【讨论】:

              【解决方案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 }
              }
              

              【讨论】:

                猜你喜欢
                • 2020-05-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-11-17
                • 1970-01-01
                • 2021-11-18
                • 2010-12-22
                • 1970-01-01
                相关资源
                最近更新 更多