iOS
中是否存在野指針的情況桃序?
野指針
野指針指向一個(gè)已刪除的對(duì)象或未申請?jiān)L問受限內(nèi)存區(qū)域的指針媒熊。特別要指出的是野指針不是空指針坟比。
Block
一提到 Block
大家肯定都知道要說的是循環(huán)引用。在 ARC
中柠衅,如果兩個(gè)對(duì)象相互持有對(duì)方菲宴,就會(huì)造成循環(huán)引用,導(dǎo)致內(nèi)存無法釋放裙顽。在 Block
中愈犹,最常用的場景則是,self
持有 block
勋颖, block
中又持有了 self
勋锤。例如下方一段代碼:
@property (nonatmaic, copy) Block dataChanged;
- (void)setUpModel{
XYModel *model = [XYModel new];
model.dataChanged = ^(NSString *title) {
self.titleLabel.text = title;
};
self.model = model;
}
上面的這段代碼就會(huì)造成循環(huán)引用叁执。那我們怎么破除呢?通常的做法都是使用 weakSelf
來處理谈宛,即:
- (void)setUpModel {
XYModel *model = [XYModel new];
__weak typeof(self) weakSelf = self;
model.dataChanged = ^(NSString *title) {
weakSelf.titleLabel.text = title;
};
self.model = model;
}
或許你還看到另外一種不是很一樣的版本:
- (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;
}
對(duì)比一下吆录,多了一個(gè) strongSelf
。那為什么又要多加一個(gè) strongSelf
呢哀卫?
考慮一下下面的代碼撬槽,
__weak __typeof__(self) weakSelf = self;
dispatch_group_async(_operationsGroup, _operationsQueue, ^{
[weakSelf doSomething];
[weakSelf doSomethingElse];
});
在 doSomething
時(shí)恢氯, weakSelf
不會(huì)被釋放,但是在 doSomethingElse
時(shí)勋磕,weakSelf
有可能被釋放敢靡。
這個(gè)時(shí)候就遇到了野指針問題啸胧,回答了一開始的題目幔虏。
在這里就需要用到 strongSelf
贝椿,使用 __strong
確保在 Block
內(nèi), strongSelf
不會(huì)被釋放瑟蜈。
小結(jié)
在使用
Block
時(shí)铺根,如遇到循環(huán)引用問題乔宿,可以使用__weak
來破除循環(huán)引用。如果在
Block
內(nèi)需要多次訪問__weak
變量掂林,則需要使用__strong
來保持變量不會(huì)被釋放坝橡。
SDWebImage
中為什么要解碼圖片
要說明這么問題我們需要先了解一下在 iOS
中驳庭,圖片顯示的流程氯窍。
概括來說,從磁盤中加載一張圖片贝淤,并將它顯示到屏幕上政供,中間的主要工作流如下:
假設(shè)我們使用
imageWithContentsOfFile:
方法從磁盤中加載一張圖片布隔,這個(gè)時(shí)候的圖片并沒有解壓縮;然后將生成的
UIImage
賦值給UIImageView
招刨;接著一個(gè)隱式的
CATransaction
捕獲到了UIImageView
圖層樹的變化哀军;在主線程的下一個(gè)
run loop
到來時(shí),Core Animation
提交了這個(gè)隱式的transaction
谎倔,這個(gè)過程可能會(huì)對(duì)圖片進(jìn)行copy
操作片习,而受圖片是否字節(jié)對(duì)齊等因素的影響,這個(gè)copy
操作可能會(huì)涉及以下部分或全部步驟:
- 分配內(nèi)存緩沖區(qū)用于管理文件
IO
和解壓縮操作哭靖;- 將文件數(shù)據(jù)從磁盤讀到內(nèi)存中侈离;
- 將壓縮的圖片數(shù)據(jù)解碼成未壓縮的位圖形式卦碾,這是一個(gè)非常耗時(shí)的
CPU
操作;- 最后
Core Animation
使用未壓縮的位圖數(shù)據(jù)渲染UIImageView
的圖層济榨。在上面的步驟中绿映,我們提到了圖片的解壓縮是一個(gè)非常耗時(shí)的
CPU
操作,并且它默認(rèn)是在主線程中執(zhí)行的丐一。那么當(dāng)需要加載的圖片比較多時(shí)淹冰,就會(huì)對(duì)我們應(yīng)用的響應(yīng)性造成嚴(yán)重的影響樱拴,尤其是在快速滑動(dòng)的列表上,這個(gè)問題會(huì)表現(xiàn)得更加突出珍坊。
這里順便提一下 imageNamed:
和 imageWithContentsOfFile:
的區(qū)別瘪弓,這兩個(gè) API
都需要解碼,并且工作流程都是一致的袱饭。不過imageNamed:
會(huì)做緩存處理,在下一次用到相同的資源時(shí)虑乖,就會(huì)從緩存里面讀取懦趋。而 imageWithContentsOfFile:
則不會(huì)。所以網(wǎng)上大多文章都會(huì)告訴你疹味,多次使用的小圖片使用 imageNamed:
加載仅叫,一次性使用的大圖片使用 imageWithContentsOfFile:
加載。
對(duì)于上面引用的流程中最后提到糙捺,當(dāng)有大量圖片滑動(dòng)時(shí)就會(huì)造成主線程的卡頓诫咱,原因就是解碼圖片在主線程中操作的。那有什么辦法避免呢洪灯?
我在查詢關(guān)于這個(gè)問題的相關(guān)資料時(shí)坎缭,發(fā)現(xiàn)有些博客給出了2種方案:
我們不使用
imageNamed:
加載圖片,使用其他的方法掏呼,比如imageWithContentsOfFile:
我們自己解碼圖片,可以把這個(gè)解碼過程放到子線程
其實(shí)第一種方式?jīng)]法避免卡頓铅檩。這就引出了為什么 SDWebImage
中需要自己解碼圖片憎夷。
在我們使用
UIImage
的時(shí)候,創(chuàng)建的圖片通常不會(huì)直接加載到內(nèi)存昧旨,而是在渲染的時(shí)候再進(jìn)行解壓并加載到內(nèi)存拾给。這就會(huì)導(dǎo)致UIImage
在渲染的時(shí)候效率上不是那么高效。為了提高效率通過decodedImageWithImage
方法把圖片提前解壓加載到內(nèi)存兔沃,這樣這張新圖片就不再需要重復(fù)解壓了鸣戴,提高了渲染效率。這是一種空間換時(shí)間的做法粘拾。
參考文章:
-
到底什么時(shí)候才需要在ObjC的Block中使用weakSelf/strongSelf
浮生獵趣 - 談?wù)?iOS 中圖片的解壓縮 雷純鋒的技術(shù)博客
- SDWebImage源碼解析(三)——SDWebImage圖片解碼/壓縮模塊 SHY圓圓圈圈圓圓