32bit設(shè)備上關(guān)于NSNumber的一個(gè)大坑以及OBJC_ASSOCIATION_ASSIGN可能引起的Crash

問題現(xiàn)象:

<br />有這樣兩個(gè)方法:

- (ErrorViewType)viewType {
    NSNumber *number = objc_getAssociatedObject(self, @selector(viewType));
    return [number integerValue];
}
 
l
- (void)setViewType:(ErrorViewType)viewType {
    objc_setAssociatedObject(self, @selector(viewType),@(viewType), OBJC_ASSOCIATION_ASSIGN) ;
}

其中ErrorViewType為枚舉變量類型NSUInteger,值為0-14

有這樣兩個(gè)方法在验,某一次setViewType傳入的值為14掌敬,但是在之后某次get的時(shí)候直接崩了包券。報(bào)的錯(cuò)誤是EXC_BAD_ACCESS介袜,即野指針訪問痹兜,那么這個(gè)對(duì)象又是什么時(shí)候被釋放蛮粮,又為什么被釋放了呢益缎?

寫了一個(gè)簡(jiǎn)單的Demo在32bit真機(jī)上驗(yàn)證了一下,iPhone4s,iOS7系統(tǒng):

crash截圖.png

可見當(dāng)被關(guān)聯(lián)的對(duì)象NSNumer的值為13的時(shí)候然想,在下一輪runloop去訪問這個(gè)對(duì)象會(huì)引發(fā)野指針訪問:
編譯器的代碼大概是莺奔,通過rewrite以及匯編代碼整理:

id tmp1 = _objc_retainAutoreleasedReturnValue([NSNumber numberWithInt:13]);//ARC環(huán)境下對(duì)于autorelease對(duì)象的優(yōu)化 這個(gè)時(shí)候tmp1指向的對(duì)象retainCount為1
_objc_setAssociatedObject(self,_SEL(testMagicNumber),tmp1,0);
_objc_release(temp1);//因?yàn)閠emp是作為一個(gè)入?yún)簵5模@里出了上面方法的作用域就會(huì)被釋放
id tmp2 = _objc_retainAutoreleasedReturnValue(_objc_getAssociatedObject(self,_SEL(testMagicNumber)));
objc_storeStrong(&tmp2,nil);//release temp2指向的對(duì)象变泄,并且把temp2指向nil

其中對(duì)于ARC下對(duì)autorelease對(duì)象的優(yōu)化可以參考我上一篇博客:
ARC環(huán)境下編譯器到底對(duì)autorelease對(duì)象做了怎樣的優(yōu)化

可見temp1指向的對(duì)象在_objc_setAssociatedObject方法調(diào)用結(jié)束之后就立即被釋放了令哟,而對(duì)于關(guān)聯(lián)對(duì)象的修飾符又是assign,沒有被強(qiáng)引用妨蛹,所以最后temp1指向的對(duì)象release1次引用計(jì)數(shù)就變成了0励饵,也就隨即被釋放了。所以下一輪runloop訪問的時(shí)候就引發(fā)野指針訪問的崩潰了滑燃。在當(dāng)前runloop訪問的話也會(huì)崩潰,但是如果把上面的set和get方法抽出來之后現(xiàn)象有點(diǎn)詭異颓鲜,這個(gè)待會(huì)說表窘。

然后我們把NSNumer的值改為12之后典予,一切正常, 這其中有什么貓膩呢乐严。

http://stackoverflow.com/questions/2533355/nsnumber-13-wont-retain-everything-else-will
上面這個(gè)回答里有提到瘤袖,NSNumber在32bit設(shè)備之上0-12都是存在內(nèi)存共享區(qū),類似于[NSArray array]無論調(diào)用多少次指針指向的都是相同的一塊內(nèi)存區(qū)域昂验,永遠(yuǎn)不會(huì)被銷毀捂敌。而只要大于12就是正常的創(chuàng)建在堆上的對(duì)象。為了驗(yàn)證這個(gè)問題既琴,再寫個(gè)Demo占婉,還是4s真機(jī)測(cè)試:

32bit magic number test

果然,驗(yàn)證了猜想甫恩,在iPhone5 iOS10系統(tǒng)上同樣的現(xiàn)象逆济。

同時(shí)也發(fā)現(xiàn)了一個(gè)有意思的現(xiàn)象:

封裝get方法之后不會(huì)崩潰.png

將get操作封裝在一個(gè)方法中,不會(huì)崩潰磺箕。這個(gè)原因暫時(shí)沒想通奖慌,但是只要點(diǎn)了Xcode的停止按鈕,還是會(huì)提示有Crash

奇葩的崩潰

在get操作之后再打印一下取到的對(duì)象松靡,這種case下就會(huì)崩潰简僧,然后取到的對(duì)象居然是@(20),也就是雖然@(14)被釋放了雕欺,但是在他的內(nèi)存區(qū)域由重新new了一個(gè)@(20)的對(duì)象岛马,鳩占鵲巢,@(20)這個(gè)對(duì)象被釋放之后20這個(gè)值還是存在的阅茶,所以還能夠打印出來蛛枚,但是因?yàn)閳?zhí)行的對(duì)象以及被銷毀,所以再對(duì)其調(diào)用release方法就自然會(huì)報(bào)double free的錯(cuò)誤了脸哀。

這里到底為什么蹦浦,一塊內(nèi)存被銷毀之后內(nèi)存中到底是如何標(biāo)記的,拋磚引玉一下撞蜂,歡迎大神指導(dǎo)~

那么對(duì)于64bit的設(shè)備呢盲镶?

64bit magic number test1.png
64bit magic number test2.png

可見無論NSInterger保存的這個(gè)數(shù)有多大,只要在正常范圍之內(nèi)蝌诡,一定是存放在常量區(qū)的溉贿,也就是永遠(yuǎn)不會(huì)釋放。

可見浦旱,是系統(tǒng)在32bit設(shè)備上對(duì)NSNumber類型的對(duì)象做的優(yōu)化不夠徹底宇色,然后我們?cè)谑褂藐P(guān)聯(lián)對(duì)象時(shí)內(nèi)存修飾符又使用不當(dāng),造成了崩潰的問題。猜測(cè)對(duì)于32bit的設(shè)備宣蠕,同時(shí)存在大量的共享內(nèi)存會(huì)比較消耗資源例隆,因此只對(duì)0-12這少數(shù)的幾個(gè)數(shù)做了優(yōu)化,而出問題時(shí)候我們傳入的參數(shù)剛好是14抢蚀,所以就掉進(jìn)了坑里镀层。

解決辦法及結(jié)論

OBJC_ASSOCIATION_ASSIGN改為OBJC_ASSOCIATION_RETAIN,這樣在本對(duì)象有一個(gè)強(qiáng)引用皿曲,這個(gè)被關(guān)聯(lián)的對(duì)象也就不會(huì)釋放唱逢,生命周期也和本對(duì)象相同了。我認(rèn)為既然關(guān)聯(lián)對(duì)象傳入的都是對(duì)象屋休,那么其實(shí)絕大多時(shí)候用的都應(yīng)該是是OBJC_ASSOCIATION_RETAIN坞古,在我們項(xiàng)目中傳入的對(duì)象很多是NSNumber類型(包裝的bool或則int)的時(shí)候都是用的OBJC_ASSOCIATION_ASSIGN,以前沒暴露問題也是誤打誤撞錯(cuò)進(jìn)錯(cuò)出博投。所以除了一些需要破解循環(huán)引用的場(chǎng)景绸贡,關(guān)聯(lián)對(duì)象的內(nèi)存操作修飾符建議都用OBJC_ASSOCIATION_RETAIN

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末毅哗,一起剝皮案震驚了整個(gè)濱河市听怕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌虑绵,老刑警劉巖尿瞭,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異翅睛,居然都是意外死亡声搁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門捕发,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疏旨,“玉大人,你說我怎么就攤上這事扎酷¢芾裕” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵法挨,是天一觀的道長(zhǎng)谁榜。 經(jīng)常有香客問我,道長(zhǎng)凡纳,這世上最難降的妖魔是什么窃植? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮荐糜,結(jié)果婚禮上巷怜,老公的妹妹穿的比我還像新娘葛超。我一直安慰自己,他們只是感情好延塑,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布巩掺。 她就那樣靜靜地躺著,像睡著了一般页畦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上研儒,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天豫缨,我揣著相機(jī)與錄音,去河邊找鬼端朵。 笑死好芭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的冲呢。 我是一名探鬼主播舍败,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼敬拓!你這毒婦竟也來了邻薯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤乘凸,失蹤者是張志新(化名)和其女友劉穎厕诡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體营勤,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡灵嫌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了葛作。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寿羞。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赂蠢,靈堂內(nèi)的尸體忽然破棺而出绪穆,到底是詐尸還是另有隱情,我是刑警寧澤客年,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布霞幅,位于F島的核電站,受9級(jí)特大地震影響量瓜,放射性物質(zhì)發(fā)生泄漏司恳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一绍傲、第九天 我趴在偏房一處隱蔽的房頂上張望扔傅。 院中可真熱鬧耍共,春花似錦、人聲如沸猎塞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荠耽。三九已至钩骇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铝量,已是汗流浹背倘屹。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留慢叨,地道東北人纽匙。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像拍谐,于是被迫代替她去往敵國(guó)和親烛缔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容