我在iOS中处理大对象时通常使用"faulting"
或lazy initialization
模式。
每当一个类有一个指向"胖"对象的属性时,我都会创建一个自定义getter来检查iVar
是否为nil
。如果是,它会创建fat对象并返回它。如果不是,它只返回"fat"对象。
此属性的容器还订阅内存警告,当收到内存警告时,它会将iVar
设置为nil
,减少内存占用。正如你所看到的,这与核心数据中的断层非常相似。
我正试图在Swift中重现这一点,但到目前为止还没有找到一个像样、优雅的解决方案。
a)第一次尝试:延迟存储属性
这不起作用,因为如果我将属性设置为零,它将永远保持为零。"魔法"只发生在你第一次访问房产时:
struct FatThing{
// I represent something big, which might have to be
// "faulted" (set to nil) when a memory warning
// is received
var bigThing = "I'm fat"
}
class Container {
lazy var fat: FatThing? = FatThing()
}
var c = Container()
c.fat
c.fat = nil
c.fat // returns nil
b)第二次尝试:具有观察者的存储属性
这也失败了,因为缺少get观察员。我需要一个willGet
和didGet
,而不仅仅是willSet
和didSet
。
为什么地球上没有得到obeserver?这个被称为观察者的半推半就的东西有什么用?
c)第三次尝试:使用存储的辅助属性的计算属性
这是我迄今为止发现的唯一可行的选择,但它就像狒狒的屁股一样丑陋!
struct FatThing{
// I represent something big, which might have to be
// "faulted" (set to nil) when a memory warning
// is received
var bigThing = "I'm fat"
}
class Container {
private var _fat : FatThing? // having this extra and exposed var kills my inner child
var fat: FatThing? {
get{
if _fat == nil {
_fat = FatThing()
}
return _fat
}
set{
_fat = newValue
}
}
}
var c = Container()
c.fat
c.fat = nil
c.fat // returns FatThing
这么多让我的代码看起来更简单、更短。。。
有没有一种简单而优雅的方法来实现这一点?在iOS这样一个缺乏记忆的环境中,这不是什么新奇的东西!
单独覆盖getter或setter的能力是目标C的一个特点,在swift中没有对应的功能。
您可以选择的正确选项是第3种,使用backing属性存储脂肪数据,并使用计算属性使其可访问。我同意有一些样板代码,但这是为了获得所需的东西。
然而,如果你经常使用这种模式,那么你可以创建一个协议:
protocol Instantiable {
init()
}
并在您的FatThing
结构/类中实现它。接下来,创建一个包含样板代码的通用函数:
func lazyInitializer<T: Instantiable>(inout property: T?) -> T {
if property == nil {
property = T()
}
return property!
}
请注意,T
必须实现Instantiable
,这允许您使用无参数构造函数创建实例。
最后,使用如下:
private var _fat : FatThing? // having this extra and exposed var kills my inner child
var fat: FatThing? {
get { return lazyInitializer(&self._fat) }
set { _fat = newValue }
}
请注意,在您的代码中,您不必将fat
计算机属性声明为可选的-您要确保在get
实现中它始终不是零,因此更好的实现是:
var fat: FatThing {
get{
if _fat == nil {
_fat = FatThing()
}
return _fat!
}
set{
_fat = newValue
}
}