Swift 中的错误或"lazy initialisation"模式



我在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观察员。我需要一个willGetdidGet,而不仅仅是willSetdidSet

为什么地球上没有得到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
    }
}

相关内容

  • 没有找到相关文章

最新更新