延時載入或者說延時初始化是很常用的最佳化方法,在構建和產生新的對象的時候,記憶體配置會在運行時耗費不少時間,如果有一些對象的屬性和內容非常複雜的話,這個時間更是不可忽略。另外,有些情況下我們並不會立即用到一個對象的所有屬性,而預設情況下初始化時,那些在特定環境下不被使用的儲存屬性,也一樣要被初始化和賦值,也是一種浪費。在其他語言 (包括 Objective-C) 中延時載入的情況是很常見的。我們在第一次訪問某個屬性時,判斷這個屬性背後的儲存是否已經存在,如果存在則直接返回,如果不存在則說明是首次訪問,那麼就進行初始化並儲存後再返回。這樣我們可以把這個屬性的初始化時刻延遲,與包含它的對象的初始化時刻分開,以達到提升效能的目的。以 Objective-C 舉個例子 (雖然這裡既沒有費時操作,也不會因為使用延時載入而造成什麼效能影響,但是作為一個最簡單的例子,可以很好地說明問題):// ClassA.h@property (nonatomic, copy) NSString *testString;// ClassA.m- (NSString *)testString { if (!_testString) { _testString = @"Hello"; NSLog(@"只在首次訪問輸出"); } return _testString;}在初始化 ClassA 對象後,_testString 是 nil。只有當首次訪問 testString 屬性時 getter 方法會被調用,並檢查如果還沒有初始化的話,就進行賦值。為了方便確認,我們還在賦值時列印了一句 log。我們之後再多次訪問這個屬性的話,因為 _testString 已經有值,因此將直接返回。在 Swift 中我們使用在變數屬性前加 lazy 關鍵字的方式來簡單地指定延時載入。比如上面的的代碼我們在 Swift 中重寫的話,會是這樣:class ClassA { lazy var str: String = { let str = "Hello" print("只在首次訪問輸出") return str }()}我們在使用 lazy 作為屬性修飾符時,只能聲明屬性是變數。另外我們需要顯式地指定屬性類型,並使用一個可以對這個屬性進行賦值的語句來在首次訪問屬性時運行。如果我們多次訪問這個執行個體的 str 屬性的話,可以看到只有一次輸出。為了簡化,我們如果不需要做什麼額外工作的話,也可以對這個 lazy 的屬性直接寫指派陳述式:lazy var str: String = "Hello" 相比起在 Objective-C 中的實現方法,現在的 lazy 使用起來要方便得多。另外一個不太引起注意的是,在 Swift 的標準庫中,我們還有一組 lazy 方法,它們的定義是這樣的:func lazy<S : SequenceType>(s: S) -> LazySequence<S>func lazy<S : CollectionType where S.Index : RandomAccessIndexType>(s: S) -> LazyRandomAccessCollection<S>func lazy<S : CollectionType where S.Index : BidirectionalIndexType>(s: S) -> LazyBidirectionalCollection<S>func lazy<S : CollectionType where S.Index : ForwardIndexType>(s: S) -> LazyForwardCollection<S>這些方法可以配合像 map 或是 filter 這類接受閉包並進行啟動並執行方法一起,讓整個行為變成延時進行的。在某些情況下這麼做也對效能會有不小的協助。例如,直接使用 map 時:let data = 1...3 let result = data.map { (i: Int) -> Int in print("正在處理 \(i)") return i * 2}print("準備訪問結果") for i in result { print("操作後結果為 \(i)")}print("操作完畢") 這麼做的輸出為:// 正在處理 1// 正在處理 2// 正在處理 3// 準備訪問結果// 操作後結果為 2// 操作後結果為 4// 操作後結果為 6// 操作完畢而如果我們先進行一次 lazy 操作的話,我們就能得到延時運行版本的容器:let data = 1...3 let result = data.lazy.map { (i: Int) -> Int in print("正在處理 \(i)") return i * 2}print("準備訪問結果") for i in result { print("操作後結果為 \(i)")}print("操作完畢") 此時的運行結果:// 準備訪問結果// 正在處理 1// 操作後結果為 2// 正在處理 2// 操作後結果為 4// 正在處理 3// 操作後結果為 6// 操作完畢