自己實現(xiàn)swift lazy關(guān)鍵字效果

Lazy

lazy關(guān)鍵字的作用是在第一次使用屬性的時候才去生成,而不是在一開始就初始化好,按需使用。
當計算屬性的值比較耗時,或者需要外部值的依賴時,用lazy比較合適。

struct Animal {
    lazy var name: String = {
        // ...
        return ...
    }()
}

lazy必須配合var使用,因為let需要有個初始值。

當使用lazy屬性時,struct對象必須聲明為var,因為在訪問屬性時會改變該對象。如果像下面這樣,會報錯。

let animal = Animal()
print(animal.name)
// error: Cannot use mutating getter on immutable value: 'animal' is a 'let' constant.

global和static修飾的屬性都是lazy的。

自己實現(xiàn)lazy

知道了lazy代表的意思,我們可以自己實現(xiàn)lazy。主要思路就是判斷該值是否計算過,如果沒有,就計算,返回結(jié)果,如果已經(jīng)計算過了,就直接返回結(jié)果。下面將會用到enum關(guān)聯(lián)值和簡單的模式匹配。

定義

首先來定義enum

private enum LazyValue<T> {
    case NotComputed(() -> T)
    case Computed(T)
}

如果未計算過,就用func計算,并且將result關(guān)聯(lián)到Computed(T)中。

class LazyBox<T> {
    init(computation: () -> T) {
        _value = .NotComputed(computation)
    }

    private var _value: LazyValue<T>

    var value: T {
        switch self._value {
        case .NotComputed(let computation):
            let result = computation()
            self._value = .Computed(result)
            return result
        case .Computed(let result):
            return result
        }
    }
}

這里利用了一個中間值_value來存儲其狀態(tài),當要真正獲取value的時候,判斷_value關(guān)聯(lián)屬性,然后進行處理。

Test
var counter = 0
let box = LazyBox<Int> {
    counter++
    return counter * 10
}

assert(box.value == 10)
//assertion failed
assert(box.value == 20)
assert(counter == 1)

LazyBox所關(guān)聯(lián)的函數(shù)只會執(zhí)行一次,所以assert(box.value == 20)
不成立。

小優(yōu)化

每次取值都是box.value,顯得有些不太方便,所以我們再提供一個直接屬性來獲取value。

struct Animal {
    private let _name = LazyBox<String> {
        return "animal"
    }
    
    var name: String {
        return _name.value
    }
}

let animal = Animal()
animal.name
并發(fā)

如果在多線程情況下,在計算value時是不安全的。有可能出現(xiàn)計算多次的情況。為了解決這個問題,可以用同步隊列,一次只能一個線程訪問,保證只會計算一次。

class LazyBox<T> {
    
    private var _value: LazyValue<T>
    
    private let queue = dispatch_queue_create("LazyBox", DISPATCH_QUEUE_SERIAL)
   
    init(computation: () -> T) {
        _value = .NotComputed(computation)
    }
    
    var value: T {
        var returnValue: T? = nil

        dispatch_sync(queue) {
            switch self._value {
                
            case .NotComputed(let computation):
                
                let result = computation()
                self._value = .Computed(result)
                returnValue = result
                
            case .Computed(let result):
                returnValue = result
            }
        }
        
        return returnValue!
    }
}

這可能會有點性能下降,因為在讀取的時候同樣也是在同步隊列中讀取,但是影響不會很大。

蘋果文檔中也說道,lazy進行計算時是線程不安全的。

Note: If a property marked with the lazy modifier is accessed by multiple threads simultaneously and the property has not yet been initialized, there is no guarantee that the property will be initialized only once.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,578評論 19 139
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan閱讀 4,503評論 2 7
  • 基礎(chǔ)部分(The Basics) 當推斷浮點數(shù)的類型時,Swift 總是會選擇Double而不是Float。 結(jié)合...
    gamper閱讀 1,496評論 0 7
  • xmind 總結(jié) 繼承NSObject類,可以對父類的構(gòu)造函數(shù)重寫;也可以使用KVC功能 參數(shù)引用不是強引用 創(chuàng)建...
    CoderZXS閱讀 125評論 0 0
  • 這些年因為大家健康意識的增強,養(yǎng)生講座隨之走紅。但很多人對養(yǎng)生的概念比較模糊,以為養(yǎng)生就是“有病治病、無病...
    楊靜相伴要你好看閱讀 464評論 0 1

友情鏈接更多精彩內(nèi)容