Lazy read-only properties

I frequently use lazily loaded read-only properties. A typical use case for this would be a class that has an icon image, which should only be loaded once it is requested. The Obj-C code would typically look like this:


@interface DocumentItem : NSObject
@property(readonly) NSImage *icon;
@end


@implementation DocumentItem
-(NSImage*)icon {
     if (!_icon) _icon = [NSImage imageNamed:@"DocumentIcon"];
     return _icon;
}
@end


I saw that Swift has built-in support for lazy loading and was thrilled!


class DocumentItem : NSObject {
     lazy var icon : NSImage? = NSImage(imageNamed:"DocumentIcon")
}


Much shorter! But, it turns out that this isn't read-only, and I can't seem to override it in a subclass. If I try, I get a cryptic error about conflicting objective C selectors related to setIcon: or something.


What I really want is to use "let" instead of "var", but for some reason this isn't possible


class DocumentItem : NSObject {
     lazy let icon : NSImage? = NSImage(imageNamed:"DocumentIcon")
}


Someone on Stack Overflow suggested using a private setter:


class DocumentItem : NSObject {
     private(set) lazy var icon : NSImage? = NSImage(imageNamed:"DocumentIcon")
}


But that still doesn't let me override the property in a subclass.


Is there some way to tell Swift to either make a "var" property readonly, or to make a "let" property lazy?

Seems to work for me:


import Cocoa

class Foo: NSObject {
  private(set) lazy var icon : NSImage? = NSImage()
}


class Bar: Foo {
  override var icon : NSImage? {
      get { return NSImage() }
      set { fatalError("Don't call this!") }
  }
}

Hm. Obviously I wanted to override the lazy property with another property like this:


import Cocoa
class DocumentItem: NSObject {
  lazy var icon : NSImage? = NSImage(imageNamed:"DocumentIcon")
}
class SpecialDocumentItem: DocumentItem {
  override lazy var icon : NSImage? = NSImage(imageNamed:"SpecialDocumentItem")
}


However, that doesn't work. Looking at the error messages, maybe I'm doing something wrong; since it seems that overriding vars just isn't possible.


A solution I found is to use computed properties, since I can override them just fine, and do the lazy loading somewhere else:


let documentIcon : NSImage? = NSImage(imageNamed:"DocumentIcon")
let specialDocumentIcon : NSImage? = NSImage(imageNamed:"SpecialDocumentItem")

class DocumentItem: NSObject {
  var icon : NSImage? { return documentIcon }
}
class SpecialDocumentItem: DocumentItem {
  override var icon : NSImage? { return specialDocumentIcon }
}


Since global constants are always loaded lazily, it seems that this does what I want. Unfortunately this does spread the code out, so it makes it harder to follow, but it's almost as clean as the Obj-C version

Lazy read-only properties
 
 
Q