Swift 中的延迟属性初始化
Posted
技术标签:
【中文标题】Swift 中的延迟属性初始化【英文标题】:Lazy property initialization in Swift 【发布时间】:2014-06-08 15:53:40 【问题描述】:您将如何在 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】:这个呢:
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
【讨论】:
以上是关于Swift 中的延迟属性初始化的主要内容,如果未能解决你的问题,请参考以下文章