[swift-evolution] lazy keyword vs lazy initialization pattern

David Hart david at hartbit.com
Fri Dec 4 07:40:39 CST 2015


In Objective-C, I often used the lazy initialization pattern to implement a cache for expensive operations. For exemple, here is an often used scenario in a project where objects behind foreign keys in a database ORM are only fetched when necessary:

@interface MyClass : NSObject

@property (nonatomic) ForeignClass* foreignObject;
@property (nonatomic) int64_t foreignKey;

@end

@implementation MyClass

- (void)setForeignKey:(int64_t)foreignKey {
   _foreignKey = foreignKey;
   _foreignObject = nil;
}

- (ForeignClass*)foreignObject {
   if (!_foreignObject) {
       _foreignObject = [Database expensiveSelect:_foreignKey];
   }
   return _foreignObject;
}

@end

Unfortunately, the lazy keyword in Swift, which was supposed to make the lazy initialization pattern more concsive does not work in this case:

class MyClass {
   var foreignKey: Int64 {
       didSet {
           self.foreignObject = nil
       }
   }
   
   lazy var foreignObject: ForeignClass? = {
       return Database.expensiveSelect(self.foreignKey)
   }()
}

I'm forced to rewrite it this way:

class MyClass {
   var foreignKey: Int64 {
       didSet {
           self.foreignObject = nil
       }
   }
   
   private var _foreignObject: ForeignClass? = nil
   var foreignObject: ForeignClass? {
       if _foreignObject == nil {
           _foreignObject = Database.expensiveSelect(self.foreignKey)
       }
       return _foreignObject
   }
}

When thinking about it, I came to the conclusion that the use cases of lazy seem very narrow compared to how useful the lazy initialization pattern was in Objective-C.
I want your opinion on three alternatives:

1- Do nothing, and use the slightly uglier Swift example when using a cache.
2- Modify lazy semantics to re-calculates when nil (I think this is the worst solution).
3- Add a cache modifier that re-calcualtes when nil.


More information about the swift-evolution mailing list