错误的初始化"懒惰的初始化"斯威夫特的模式

时间:2014-11-02 14:54:34

标签: ios swift instance-variables lazy-initialization

在处理大型对象时,我通常在iOS中使用"faulting"lazy initialization模式。

每当一个类的属性指向" fat"对象,我创建了一个自定义getter,用于检查iVar是否为nil。如果是,则创建fat对象并返回它。如果它不是,它只会返回"脂肪"对象

此属性的容器还订阅了内存警告,当收到内存警告时,它会将iVar设置为nil减少内存占用。如您所见,它与Core Data中的错误非常相似。

我试图在Swift中重现这一点,但到目前为止还没有找到一个体面和优雅的解决方案。

a)首次尝试:延迟存储的属性

这不起作用,因为如果我将属性设置为nil,它将永远保持为零。 "魔术"只有在您第一次访问该属性时才会发生:

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)第二次尝试:带有观察员的存储属性

由于缺乏获取观察者,这也失败了。我需要willGetdidGet,而不仅仅是willSetdidSet

为什么地球上没有任何观察者?这个半支持的东西叫做观察者的用途是什么?

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这样的内存不足的环境中,这不是异国情调!

1 个答案:

答案 0 :(得分:3)

单独覆盖getter或setter的能力是客观C的特性,在swift中没有对应物。

您可以使用的正确选择是否定的。 3,使用backing属性存储fat数据和计算属性以使其可访问。我同意有一些样板代码,但这是你需要的权衡。

但是,如果经常使用该模式,则可以创建协议:

protocol Instantiable {
    init()
}

并在FatThing struct / class中实现它。接下来,创建一个包含样板代码的通用函数:

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实现中确保它始终不是nil,因此更好的实现是:

var fat: FatThing {
    get{
        if _fat == nil {
            _fat = FatThing()
        }
        return _fat!
    }
    set{
        _fat = newValue
    }
}