前言
在iOS開發(fā)中,懶加載的角色在很多時候至關(guān)重要,它可以讓我們的代碼變得更加優(yōu)秀。之所以寫這篇文章,主要是談?wù)剛€人對懶加載的粗淺理解,如果不對的地方,歡迎各位仁人志士不吝賜教,這里只是拋磚引玉。另外,相信網(wǎng)上也有很多講述懶加載的各種文章,有的分析也甚是不錯。但是技術(shù)這種東西,還是需要思想的碰撞,才能折射出更加耀眼的精華。
懶加載的本質(zhì)
懶加載,亦叫延遲加載,即在第一次需要的時候才去加載,本質(zhì)上就是對一個實(shí)例的getter方法的重寫。
懶加載的使用
懶加載的使用也相對簡單,我們以例子來說話,為了能夠盡量詳細(xì)點(diǎn),下面會粘貼全部的代碼:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UILabel *label;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self label];
}
- (UILabel *)label {
if (!_label) {
_label = [[UILabel alloc] init];
_label.frame = CGRectMake(64, 64, 300, 50);
_label.backgroundColor = [UIColor cyanColor];
_label.text = @"懶加載就是當(dāng)需要的時候才去加載";
[self.view addSubview:_label];
}
return _label;
}
@end
上面就是一個懶加載的寫法,其它類型大同小異,可舉一反三,這里主要以上面例子為準(zhǔn)。
首先,我們來分析下,為什么在viewDidLoad方法中調(diào)用懶加載是以[self label]的方式來調(diào)用,而不是以self.label的方式呢?為了驗(yàn)證是否可以,我們將[self label]改成self.label,Xcode會提示如下
Property access result unused - getters should not be used for side effects
顯然,Xcode報(bào)了一個warning, 但是我們直接直接運(yùn)行,忽略這個warning,任何錯誤都不會產(chǎn)生,視圖正常顯示在界面上。
原因是,當(dāng)我們調(diào)用懶加載方法時,系統(tǒng)默認(rèn)其為一個getter方法,而在- (UILabel *)label這個懶加載中,必然要返回一個實(shí)例label,所以報(bào)警告說獲取到的屬性結(jié)果并未使用,如果我們在調(diào)用時并且用一個變量去接收它,那警告自然而然就會消除,如下:
UILabel *receivedLabel = self.label;
NSLog(@"%@",receivedLabel);
但是,根本原因是什么呢?事實(shí)上,根本原因是apple期望調(diào)用屬性使用點(diǎn)語法形式,而調(diào)用方法使用方括號[]的形式,而在懶加載中,盡管我們說其本質(zhì)是重寫getter方法,獲取的實(shí)例類似一個屬性,但是實(shí)際上它還是一個方法,所以Xcode希望我們以方法的形式去調(diào)用懶加載!
當(dāng)然,有人會說了,如果在懶加載中我沒有將其添加到self.view上呢,這樣我不就可以使用[self.view addSubview:self.label];點(diǎn)語法來添加了嗎?沒錯,你當(dāng)然也可以這樣寫,沒有任何問題!只是我們沒有必要這樣做而已,如果你使用方法的形式去調(diào)用,絕對不會出現(xiàn)任何問題。
懶加載的注意點(diǎn)
使用懶加載時,必須注意getter方法嵌套,從而造成死循環(huán),導(dǎo)致程序crash!, 比如,我們寫成這樣:
- (UILabel *)label {
// 注意這里會造成程序奔潰,getter方法自身嵌套循環(huán),永無休止
if (!self.label) {
_label = [[UILabel alloc] init];
_label.frame = CGRectMake(64, 64, 300, 50);
_label.backgroundColor = [UIColor cyanColor];
_label.text = @"懶加載就是當(dāng)需要的時候才去加載";
}
return _label;
}
又或者寫成
- (UILabel *)label {
if (!_label) {
_label = [[UILabel alloc] init];
_label.frame = CGRectMake(64, 64, 300, 50);
_label.backgroundColor = [UIColor cyanColor];
_label.text = @"懶加載就是當(dāng)需要的時候才去加載";
}
// 注意這里會造成程序奔潰,getter方法自身嵌套循環(huán),永無休止
return self.label;
}
所以,強(qiáng)烈建議使用懶加載時,懶加載內(nèi)部的實(shí)例變量完全采用下劃線的方式去判斷及創(chuàng)建?。?!
當(dāng)然,還有另外一種只要你記住, 就隨便你怎么寫,并且萬無一失、永遠(yuǎn)也不會造成死循環(huán)的方法,那就是懶加載的方法名不要和懶加載內(nèi)部的實(shí)例變量同名,這樣Xcode就可以準(zhǔn)確識別出你調(diào)用的是屬性還是懶加載方法,比如:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UILabel *label;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self lazyMethod];
}
- (UILabel *)lazyMethod {
if (!self.label) {
_label = [[UILabel alloc] init];
_label.frame = CGRectMake(64, 64, 300, 50);
_label.backgroundColor = [UIColor cyanColor];
_label.text = @"懶加載就是當(dāng)需要的時候才去加載";
[self.view addSubview:_label];
}
return self.label;
}
@end
我個人認(rèn)為這才是懶加載的較好的書寫方式,一目了然,并且安全可靠。
為什么要使用懶加載?
網(wǎng)上有很多文章論述使用懶加載的觀點(diǎn),我們來看一些常見的那些優(yōu)點(diǎn)
- 第一,懶加載不必寫在viewDidLoad方法里面,每個屬性的getter方法中分別負(fù)責(zé)各自的實(shí)例化處理,因此可讀性更強(qiáng)、獨(dú)立性更強(qiáng)
我個人覺得,這只是相對的,所以可讀性強(qiáng)也要看你怎么寫、寫多少,如果我們需要創(chuàng)建 100個視圖,如果這100個視圖全部使用懶加載的話,優(yōu)勢在哪?所以的可讀性更強(qiáng)嗎?獨(dú)立性更強(qiáng)嗎?使用懶加載我們都知道,每創(chuàng)建一個懶加載,代碼量會多出3-4行,如果我們創(chuàng)建100個視圖,那么就多出了300-400行?因此可讀性就變得不是那么強(qiáng)的,相反,如果我們直接使用組件化來分開創(chuàng)建視圖,然后在viewDidLoad中調(diào)用,那樣反而清晰明了、可讀性才是真的強(qiáng)。當(dāng)然,如果只是幾個視圖的情況下,那懶加載的確看起來更加清晰、可讀性更強(qiáng),那么,問題是如果只是簡單的幾個視圖,又何必使用懶加載,優(yōu)勢在哪?
此問題小結(jié):懶加載的可讀性強(qiáng)、獨(dú)立性強(qiáng)并不是絕對的,甚至微乎其微,我個人并覺得這有多大的優(yōu)勢或者說優(yōu)點(diǎn)。
- 第二,懶加載可以防止實(shí)例為nil
這個優(yōu)勢可能是有,但也只是相對的,一般正常情況下,作為一名合格的程序員(也許我并不合格,因?yàn)槲业拇a也曾因未安全檢查而奔潰過),都會對所使用的變量做安全檢查,就像防止數(shù)組越界一樣,從而導(dǎo)致程序奔潰!
當(dāng)然,在懶加載中,因?yàn)橐呀?jīng)獨(dú)立地做了安全檢查,所以后續(xù)使用時不需要再次去繁瑣和重復(fù)地校驗(yàn)是否為nil,的確也算是一個較好的選擇,但是,這點(diǎn)也談不上多大的優(yōu)勢,只是相對來說做了一個類似全局校驗(yàn)的做法而已。
這算是懶加載的一個相對的優(yōu)勢,但并不是絕對。
- 第三,懶加載節(jié)省內(nèi)存資源
這個在一定程度上是有道理的,當(dāng)且僅當(dāng)我們在需要加載的時候才調(diào)用懶加載,比如說,在網(wǎng)絡(luò)請求的時候,我們無法知道請求是否失敗,是因?yàn)榫W(wǎng)絡(luò)失敗,還是因?yàn)檎埱蟮臄?shù)據(jù)為空?沒有網(wǎng)絡(luò),我們就顯示一個網(wǎng)絡(luò)錯誤頁,而這個網(wǎng)絡(luò)錯誤頁面我們就可以使用懶加載,因?yàn)樵谝话闱闆r下,網(wǎng)絡(luò)好的時候,是不存在需要加載這個網(wǎng)絡(luò)錯誤頁面的,但是也為了以防萬一,比如說我們在使用某個app的時候,突然進(jìn)入電梯或地下室,都有可能導(dǎo)致網(wǎng)絡(luò)失敗的情況;如果網(wǎng)絡(luò)好的情況下,或許我們請求下來的數(shù)據(jù)為空,那此時就可以加載一個為空頁面,這個頁面就可以使用懶加載。
當(dāng)然,本文中的開頭例子是不需要使用懶加載的,因?yàn)橥耆珱]有必要,懶加載要根據(jù)時機(jī)、資源需要的時候才去使用懶加載,這樣才能節(jié)省內(nèi)存。再比方說,我們需要加載網(wǎng)絡(luò)請求下來的數(shù)據(jù)model,但是是分頁加載,那這時我們也可以使用懶加載,因?yàn)橛脩羯侠虞d也許只是看幾頁就不加載了,或者一些緩存數(shù)據(jù)的使用等等。
懶加載使用得當(dāng)?shù)拇_可以節(jié)省內(nèi)存資源,有一定程度上的性能優(yōu)化,這也是比較使我信服的優(yōu)點(diǎn),此外,我個人覺得也能節(jié)省某一階段的時間,那就是在調(diào)用懶加載前,如果后面一直未使用到懶加載,那么就可以節(jié)省加載
懶加載的這段時間。
什么時候使用懶加載?
一般情況下,不需要使用懶加載,懶加載未必能增強(qiáng)可讀性、獨(dú)立性,濫用反而讓可讀性適得其反。簡言之,就是在邏輯上,覺得現(xiàn)在不需要加載,而在后面某一時間段內(nèi)可能會加載,就可以考慮懶加載。
總結(jié)
懶加載的優(yōu)點(diǎn)
相對來說,如果代碼量不是很多,可讀性略強(qiáng)
相對來說,防止為nil,減少了后續(xù)使用時安全檢查的后顧之憂
使用適當(dāng),可節(jié)省內(nèi)存資源
一定程度上,節(jié)省了某一個期間內(nèi)的時間
使用得當(dāng),優(yōu)化性能,提高用戶體驗(yàn)
注意:上述所謂的優(yōu)點(diǎn)都只是相對來說,并沒有絕對,關(guān)鍵還是要看你怎么使用!所以懶加載好與不好,還是在于使用的人是誰。
懶加載的缺點(diǎn)
使用太泛濫,導(dǎo)致可讀性變差
使用不得當(dāng),可能會造成死循環(huán),導(dǎo)致crash
代碼量增多(每增加一個懶加載,代碼會平均多出3-4行)
注意: 上述所謂的缺點(diǎn)也只是相對來說,因人而異,畢竟,對于開來說,除懶加載這個技術(shù)點(diǎn)之外,任何技術(shù)上的或邏輯上的錯誤,最終的產(chǎn)出都是bug。