天天看點

Swift的lazy關鍵字–延遲加載

定義

lazy屬性就是初始值直到第一次使用的時候才執行計算的屬性,這對小記憶體的手機所産生的性能上的優化是相當可觀的。

注意:lazy屬性必須是變量(var修飾符),因為常量屬性(let修飾符)必須在初始化之前就有值,是以常量屬性不能定義為lazy。

Objective-C中的延遲加載

Objective-C并沒有在文法上支援延遲加載,通常是由程式員自己手動實作的。

示例如下:

@property (nonatomic, strong) NSArray *names;

- (NSArray *)names {
    if (!_names) {
        _names = [[NSArray alloc] init];
        NSLog(@"隻在首次通路輸出");
    }
    return _names;
}
           

說明:在初始化對象後,_names 是 nil。隻有當首次通路 names 屬性時 getter 方法會被調用,并檢查如果還沒有初始化的話,就進行指派。可以想見,控制台列印的“隻在首次通路輸出”的确隻會輸出一次。我們之後再多次通路這個屬性的話,因為 _names已經有值,是以将直接傳回。

分析:getter方法和下劃線文法對初學者并不是那麼的友好,同時屬性以及對應的getter方法空間上隔得比較遠,代碼邏輯不直覺。

Swift的延遲加載

Swift中則可以通過lazy關鍵字簡單地實作相同功能,比如上述示例代碼在Swift中實作的話:

lazy var names: NSArray = {
    let names = NSArray()
    print("隻在首次通路輸出")
    return names
}()
           

分析:相比起Objective-C中的實作,現在的lazy是在是簡單的多了,而且更加的直覺。除了上述直接在屬性後面定義閉包調用的方法以外,還可以使用執行個體方法(func)和類方法(class func)來為lazy屬性初始化添加必要的邏輯。

使用場景

延遲加載主要有以下兩個使用的場景:

1. 屬性的初始值依賴于其他的屬性值,隻有其他的屬性值有值之後才能得出該屬性的值。

2. 屬性的初始值需要大量的計算。

進階用法

在Swift标準庫中還有一組lazy方法,可以配合map、filter這類接受閉包并進行運作的方法一起,讓整個行為變成延時進行的。以下是map函數的用法(示例取自喵神的Swifter):

let data = .
let result = data.lazy.map {
    (i: Int) -> Int in
    print("正在處理 \(i)")
    return i * 
}

print("準備通路結果")
for i in result {
    print("操作後結果為 \(i)")
}

print("操作完畢")
           
// 準備通路結果
// 正在處理 1
// 操作後結果為 2
// 正在處理 2
// 操作後結果為 4
// 正在處理 3
// 操作後結果為 6
// 操作完畢
           
對于那些不需要完全運作,可能提前退出的情況,使用 lazy 來進行性能優化效果會非常有效。

參考連結:

1. 喵神:LAZY 修飾符和 LAZY 方法

2. Swiftist:Swift中的延遲加載

3. Apple developer