從兩道面試題提及

iOS 中是否存在野指針的狀況?

野指針

野指針指向一個已刪除的對象或未申請訪問受限內存區域的指針。特別要指出的是野指針不是空指針。ios

Block

一提到 Block 你們確定都知道要說的是循環引用。在 ARC 中,若是兩個對象相互持有對方,就會形成循環引用,致使內存沒法釋放。在 Block 中,最經常使用的場景則是,self 持有 blockblock 中又持有了 self 。例以下方一段代碼:緩存

@property (nonatmaic, copy) Block dataChanged;

- (void)setUpModel{
  XYModel *model = [XYModel new];
  model.dataChanged = ^(NSString *title) {
      self.titleLabel.text = title;                
  };
  self.model = model;
}
複製代碼

上面的這段代碼就會形成循環引用。那咱們怎麼破除呢?一般的作法都是使用 weakSelf 來處理,即:bash

- (void)setUpModel {
  XYModel *model = [XYModel new];
  __weak typeof(self) weakSelf = self;
  model.dataChanged = ^(NSString *title) {
      weakSelf.titleLabel.text = title;   
  };
  self.model = model;
}
複製代碼

或許你還看到另一種不是很同樣的版本:less

- (void)setUpModel {
  XYModel *model = [XYModel new];
  __weak typeof(self) weakSelf = self;
  model.dataChanged = ^(NSString *title) {
      __strong typeof(self) strongSelf = weakSelf;
      strongSelf.titleLabel.text = title;   
  };
  self.model = model;
}
複製代碼

對比一下,多了一個 strongSelf 。那爲何又要多加一個 strongSelf 呢?async

考慮一下下面的代碼,oop

__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^{
  [weakSelf doSomething];
  [weakSelf doSomethingElse];
});
複製代碼

doSomething 時, weakSelf 不會被釋放,可是在 doSomethingElse 時,weakSelf 有可能被釋放。ui

這個時候就遇到了野指針問題,回答了一開始的題目。spa

在這裏就須要用到 strongSelf ,使用 __strong 確保在 Block 內, strongSelf 不會被釋放。線程

小結

  • 在使用 Block 時,如遇到循環引用問題,可使用 __weak 來破除循環引用。指針

  • 若是在 Block 內須要屢次訪問 __weak 變量,則須要使用 __strong 來保持變量不會被釋放。

SDWebImage 中爲何要解碼圖片

要說明這麼問題咱們須要先了解一下在 iOS 中,圖片顯示的流程。

歸納來講,從磁盤中加載一張圖片,並將它顯示到屏幕上,中間的主要工做流以下:

假設咱們使用 imageWithContentsOfFile:方法從磁盤中加載一張圖片,這個時候的圖片並無解壓縮;

而後將生成的 UIImage 賦值給 UIImageView

接着一個隱式的 CATransaction 捕獲到了 UIImageView 圖層樹的變化;

在主線程的下一個 run loop 到來時,Core Animation 提交了這個隱式的 transaction ,這個過程可能會對圖片進行 copy 操做,而受圖片是否字節對齊等因素的影響,這個 copy 操做可能會涉及如下部分或所有步驟:

  • 分配內存緩衝區用於管理文件 IO 和解壓縮操做;
  • 將文件數據從磁盤讀到內存中;
  • 將壓縮的圖片數據解碼成未壓縮的位圖形式,這是一個很是耗時的 CPU 操做;
  • 最後 Core Animation 使用未壓縮的位圖數據渲染 UIImageView 的圖層。

在上面的步驟中,咱們提到了圖片的解壓縮是一個很是耗時的 CPU 操做,而且它默認是在主線程中執行的。那麼當須要加載的圖片比較多時,就會對咱們應用的響應性形成嚴重的影響,尤爲是在快速滑動的列表上,這個問題會表現得更加突出。

這裏順便提一下 imageNamed:imageWithContentsOfFile: 的區別,這兩個 API 都須要解碼,而且工做流程都是一致的。不過imageNamed:會作緩存處理,在下一次用到相同的資源時,就會從緩存裏面讀取。而 imageWithContentsOfFile: 則不會。因此網上大多文章都會告訴你,屢次使用的小圖片使用 imageNamed: 加載,一次性使用的大圖片使用 imageWithContentsOfFile: 加載。

對於上面引用的流程中最後提到,當有大量圖片滑動時就會形成主線程的卡頓,緣由就是解碼圖片在主線程中操做的。那有什麼辦法避免呢? 我在查詢關於這個問題的相關資料時,發現有些博客給出了2種方案:

  1. 咱們不使用imageNamed:加載圖片,使用其餘的方法,好比imageWithContentsOfFile:

  2. 咱們本身解碼圖片,能夠把這個解碼過程放到子線程

其實第一種方式無法避免卡頓。這就引出了爲何 SDWebImage中須要本身解碼圖片。

在咱們使用 UIImage 的時候,建立的圖片一般不會直接加載到內存,而是在渲染的時候再進行解壓並加載到內存。這就會致使 UIImage 在渲染的時候效率上不是那麼高效。爲了提升效率經過 decodedImageWithImage 方法把圖片提早解壓加載到內存,這樣這張新圖片就再也不須要重複解壓了,提升了渲染效率。這是一種空間換時間的作法。

參考文章:

  1. 到底何時才須要在ObjC的Block中使用weakSelf/strongSelf 浮生獵趣
  2. 談談 iOS 中圖片的解壓縮 雷純鋒的技術博客
  3. SDWebImage源碼解析(三)——SDWebImage圖片解碼/壓縮模塊 SHY圓圓圈圈圓圓

你也能夠關注個人公衆號,獲取更多文章。

在這裏插入圖片描述
相關文章
相關標籤/搜索