天天看點

【iOS】—— 懶加載一.懶加載的概念二.懶加載的使用三.懶加載的注意事項四.懶加載的優點五.懶加載的缺點

一.懶加載的概念

我們使用的iOS裝置其記憶體都有一定的限度,如果在程式啟動時就将程式内的所有資源(資料,圖檔,視訊)都加載完,那麼有可能耗盡我的iOS裝置的記憶體。

是以iOS出現了懶加載,懶加載也稱延遲加載(比如控制器的view的建立),就是在開發中,當程式啟動時不一次性加載所有的資源,而是在需要一些資源的時候程式才去加載這些資源(效率低,占用記憶體小),所謂的懶加載,其實就是對執行個體的

getter

方法的重寫。

用更清晰的說法懶加載就是,一個

view

的子控件 ,隻有當這個view被顯示的時候才去加載。一個

tableViewCell

中,給他設定了圖檔,他的

contentView

裡面才包含

imageView

的圖檔,隻有設定了

textLabel

的内容,才會加載這個

textLabel

二.懶加載的使用

  • 定義控件屬性,屬性必須用

    strong

    修飾。具體原因看這兩篇文章:iOS中用strong和weak來修飾成員變量的對比和【IOS學習基礎】weak和strong、懶加載、循環引用
//建立一個可變數組
@property (nonatomic, strong) NSMutableArray *dataArray;

@property (nonatomic, strong) UILabel *titleLabel;
           
至于為什麼必須使用strong的原因,strong會使修飾對象的引用計數加一,而weak不會,如果一個對象的引用計數為0的話系統就會自動将其銷毀。如果你想讓一個控件的生命周期随着你的控制器被銷毀才去釋放,那就使用

strong

;如果你僅僅是想讓它在被移除視圖之後就被銷毀,那就使用

weak

。當你使用懶加載将執行個體變量初始化後若使用weak修飾并且沒有在其

getter

方法中添加到視圖上時,即使你懶加載寫的再好,它所傳回的都是nil,即你的對象被銷毀了,你的懶加載就如同空有一樣。
  • 因為懶加載就是重寫這個屬性對應的

    getter

    方法,是以我們将要實作的邏輯放在

    getter

    中。
//重寫這個屬性
-(NSMutableArray *)dataArray {
//判斷可變字元串是否已經存在,若沒有,則進行執行個體化
    if (_dataArray == nil) {
        _dataArray = [[NSMutableArray alloc] init];
    }
    return _dataArray;
}

-(UILabel *)titleLabel {
    if (!titleLabel) {
        _titleLabel = [[UILabel alloc] init];
        _titleLabel.textAlignment = NSTextAlignmentCenter;
        _titleLabel.textColor = [UIColor blueColor];
        _titleLabel.text = @"lazyLoad";
        [self.view addSubview:_titleLabel];
    }
    return _titleLabel;
}
           

必須完全按照這種格式,因為如果将括号中任意一個

_titleLabel

換做

self.titleLabel

,所謂的點文法,就相當于調這個方法,在外部調用點文法時就形成無限循環,具體原因看下面注意事項的說明。

  • 懶加載的使用

    調用的時候要用自己重寫的

    getter

    方法。
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self titleLabel];
}


- (UILabel*)titleLabel {
    if (!_titleLabel) {
        _titleLabel = [[UILabel alloc] init];
        _titleLabel.textAlignment = NSTextAlignmentCenter;
        _titleLabel.textColor = [UIColor blueColor];
        _titleLabel.text = @"lazyLoad";
        _titleLabel.frame = CGRectMake(100, 100, 100, 30);
        [self.view sizeToFit];
        [self.view addSubview:_titleLabel];
    }
    return _titleLabel;
}
           

或者

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self.view addSubview:self.titleLabel];
}


- (UILabel*)titleLabel {
    if (!_titleLabel) {
        _titleLabel = [[UILabel alloc] init];
        _titleLabel.textAlignment = NSTextAlignmentCenter;
        _titleLabel.textColor = [UIColor blueColor];
        _titleLabel.text = @"lazyLoad";
        _titleLabel.frame = CGRectMake(100, 100, 100, 30);
        [self.view sizeToFit];
    }
    return _titleLabel;
}
           

三.懶加載的注意事項

注意一:指針循環引用問題

if (_dataArray == nil)  不可以寫成    if (self.dataArray == nil) ,不然會造成循環引用指針
    
    return _dataArray 不可以寫成return self.dataArray 不然會形成循環引用
           

_titleLabel

為成員變量,是由屬性附加生成的成員變量,而

self.titleLabel

則為調用屬性

titleLabel

的點文法,在沒有重寫

titleLabel

的點文法之前兩者傳回的是同一個東西,例如:

當調用

titleLabel

setter

方法調用的是下面的方法:

- (void)settitleLabel:(UILabel *)titleLabel{
     _titleLabel = titleLabel;
}
           

再調用

titleLabel

的點文法

self.titleLabel

調用的是下面這個方法:

- (UILabel *)titleLabel{
     return _titleLabel;
}
           

這就使調用

self.titleLabel

和直接調用

_titleLabel

的結果是相同的。

重寫之後

self.titleLabel

傳回的是懶加載初始化後的對象,而直接調用

_titleLabel

則傳回的是未初始化的對象,是以我們在使用懶加載時一定要注意自己的書寫,防止出現循環引用的問題。

注意二:關于布局的問題

如果用

Masonry

布局頁面的話,關于

frame

的代碼一定不能放在重寫的

getter

方法裡,不然會報找不到父視圖的錯誤,要放在

addSubview

後面。

Masonry

是一種非常流行的第三方布局架構,它是一個輕量級的布局架構,擁有自己的描述文法,采用更優雅的鍊式文法封裝自動布局,簡潔明了,并具有高可讀性,而且同時支援 iOS 和 Max OS X。

想要詳細了解

Masonry

可以看看這兩篇文章:iOS自動布局——Masonry詳解和iOS自動布局架構-Masonry詳解

注意三:不會出現死循環的方法:

當然,還有另外一種隻要你記住, 就随便你怎麼寫,并且萬無一失、永遠也不會造成死循環的方法,那就是懶加載的方法名不要和懶加載内部的執行個體變量同名,這樣

Xcode

就可以準确識别出你調用的是屬性還是懶加載方法,比如:

#import "ViewController.h"

@interface ViewController ()
@property (nonatomic, strong) UILabel *label;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self lazyMethod];
}

- (UILabel *)lazyMethod {
    if (self.titleLabel) {
        _titleLabel = [[UILabel alloc] init];
        _titleLabel.textAlignment = NSTextAlignmentCenter;
        _titleLabel.textColor = [UIColor blueColor];
        _titleLabel.text = @"lazyLoad";
        [self.view addSubview:_titleLabel];
    }
    return self.titleLabel;
}
@end
           

四.懶加載的優點

從上述的代碼可以看出,懶加載具有一下優點:

  • 如果代碼量不多,可讀性略強。
  • 防止了執行個體為 nil 的情況,減少了後續使用程式時對執行個體為 nil 的檢查,即不用擔心執行個體為 nil 的狀況導緻程式崩潰。
  • 适當使用懶加載可以節省記憶體資源。
  • 每個屬性的 getter 方法中分别負責各自的執行個體化方法,代碼彼此之間的獨立性強,降低耦合性。
  • 一定程度上節省了某一期間内的時間。
  • 得當使用可以優化程式性能,提高使用者體驗。

五.懶加載的缺點

每個方法的出現都不會完美,有優點就有缺點,從上述的程式我們難想出懶加載相應的缺點:

  • 使用泛濫會導緻可讀性變差,每使用一次懶加載,代碼就會增加幾行,那麼當你程式使用大量的懶加載它的代碼量就會比普通方法的代碼量多,看起來反而更加的繁瑣,可讀性自然也就沒有那麼強了。
  • 使用不當會導緻死循環,導緻crash,程式無法厘清調用的是 getter 方法還是執行個體變量,會造成死循環。
  • 代碼量增加(每增加一個懶加載,代碼會平均多出3-4行)。