一個(gè)偶現(xiàn)的Bug,并且只在iOS11發(fā)布后才出來(lái)的。
場(chǎng)景
場(chǎng)景比較簡(jiǎn)單:使用一個(gè)單例做緩存。單例中加了一個(gè)可變字典,用于緩存已經(jīng)加載過(guò)的圖片。
流程
根據(jù)圖片的名稱(chēng)從字典中查找圖片對(duì)象,當(dāng)找不到對(duì)象的時(shí)候就創(chuàng)建一個(gè)新的UIImage對(duì)象,并保存到字典中。找到對(duì)象就直接取現(xiàn)在有的緩存UIImage。
問(wèn)題
多線(xiàn)程時(shí),NSMutableDictionary取對(duì)象不是元子操作,所以,多個(gè)線(xiàn)程請(qǐng)求同一個(gè)圖片對(duì)象時(shí)都得到nil。然后就都跑去創(chuàng)建UIImage對(duì)象,并都試圖保存。創(chuàng)建UIImage對(duì)象時(shí),使用imageNamed方法,系統(tǒng)做了優(yōu)化,進(jìn)行緩存處理了。
NSMutableDictionary中添加相同的key的時(shí)候,會(huì)被原來(lái)的對(duì)象移除。第一個(gè)線(xiàn)程保存結(jié)束后,此時(shí)如果第二個(gè)線(xiàn)程試圖添加的對(duì)象正是第一個(gè)線(xiàn)程剛放進(jìn)去的對(duì)象,會(huì)出現(xiàn)這樣的流程:判斷字典中是否有key時(shí)成立,然后將對(duì)象移除,內(nèi)存回收,再往字典里寫(xiě)對(duì)象時(shí),就等于設(shè)置了一個(gè)野指針對(duì)象。
其它問(wèn)題
雖然聽(tīng)說(shuō)使用imageNamed,系統(tǒng)對(duì)自己做緩存,可之前試過(guò),對(duì)于同一張圖片,所創(chuàng)建的新UIImage對(duì)象的內(nèi)存地址都不一樣~所以,=。=