場景
UITableViewCell B繼承自 UITableViewCell A蜜笤, UITableViewCell A 注冊了名為A的通知靴迫,通知綁定的方法為 方法 A惕味;UITableViewCell B 注冊了名為B的通知,通知綁定的方法為 方法 B玉锌。
問題
點擊進入UITableViewCell B中后返回名挥,再進入到 UITableViewCell A中,觸發(fā)通知A主守,此時會崩潰禀倔,崩潰在 UITableViewCell B 的方法B中榄融,( 這里說一下,方法B和方法A是一樣的)
分析原因
如圖所示:UITableViewCell A和UITableViewCell B的關(guān)系和方法的調(diào)用關(guān)系大致如此蹋艺,崩潰的原因是剃袍,由于 方法B和方法A是一樣的,UITableViewCell B 繼承與 UITableViewCell A捎谨,由于
UITableViewCell B在初始化的時候調(diào)用了 UITableViewCell A中的初始化方法,所以由于繼承的機制憔维,實際上 UITableViewCell B注冊了兩個通知:通知A和通知B涛救。由于方法B和方法A是一樣的,所以UITableViewCell B中的通知A調(diào)用方法A的時候业扒,實際上就調(diào)用了方法B检吆,(當子類的方法列表中有和父類的方法列表中的方法一樣的情況下,會調(diào)用子類中的方法程储,而不調(diào)用父類中的方法蹭沛,也就是重寫),而實際上 UITableViewCell B 中的方法B設(shè)計上不是為 通知A服務(wù)的章鲤,其中調(diào)用的一些未知的數(shù)據(jù)摊灭,所有就出現(xiàn)了崩潰。
有一個問題:為什么從 UITableViewCell B中POP出后败徊,UITableViewCell B沒有被釋放呢帚呼?,就是因為UITableViewCell B沒有在頁面被 POP后被釋放掉皱蹦,才會出現(xiàn)這樣的 Crash煤杀,那么為什么沒被釋放呢
dealloc的不被調(diào)用的情況。
ARC下,控制器在被pop后移出棧后會被釋放沪哺,但有些時候會發(fā)現(xiàn)控制器出棧的時候不會調(diào)用dealloc方法沈自,系統(tǒng)可以幫我們釋放該對象,及其包含的對象;但是卻無法釋放不屬于該對象的一些東西,就造成了 對象的dealloc方法不被調(diào)用。而且重寫該方法時不能顯式調(diào)用[super dealloc]辜妓,和繼承中先加載父類再加載子類相反枯途,注銷時先注銷子類之后再注銷父類。因為系統(tǒng)會自動幫你調(diào)用父類的dealloc方法嫌拣。
- 1.通知的觀察者,或KVO的觀察者
由于通知中心是系統(tǒng)的一個單例,你在注冊通知的觀察者時,實際上是在通知中心注冊的,
這時,即使ARC下系統(tǒng)幫我們釋放了對象,但是在通知中心的觀察還是沒有移除,那么當有
該通知時,依然會嘗試調(diào)用該對象的接受通知的方法,這可能會導致一些問題.
- 2.對象強委托
對于其他的對象來把你當做委托 delegate時,并且是 強引用時,即時你自身被釋放,但是引用你的對象依然還在,這時需要在引用你的對象移除該delegate
- 3.一些其它的資源柔袁,類似地圖頁面。C語言寫的一些好內(nèi)存的類文件异逐,
- 4.控制器中NSTimer沒有被銷毀
當viewController中存在NSTimer時捶索,需要特別注意,當調(diào)用
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES]
時灰瞻,因為 target:self 腥例,也就是引用了當前viewController辅甥,導致控制器的引用計數(shù)加1,如果沒有將這個NSTimer 銷毀燎竖,它將一直保留該viewController璃弄,無法釋放,也就不會調(diào)用dealloc方法构回。所以夏块,需要在viewWillDisappear之前需要把控制器用到的NSTimer銷毀.
[timer invalidate]; // 銷毀
timertimer = nil; // 置nil
- 5.viewController中block的循環(huán)引用在ARC下,
block會把它里面的所有對象強引用纤掸,包括當前控制器self脐供,因此有可能會出現(xiàn)循環(huán)引用的問題。比如viewController中有個block屬性借跪,在block中又強引用了self或者其他成員變量政己,那么這個viewController與自己的block屬性就形成循環(huán)引用,導致viewController無法釋放掏愁。
很顯然歇由,UITableViewCell B不被釋放是因為在初始化的時候注冊的通知沒有移除,也沒有機會移除了果港,造成的每創(chuàng)建一個UITableViewCell B 都不會被釋放沦泌,而是一直在內(nèi)存中。
驗證猜想
我們修改 方法B 使方法A和 方法B不一樣京腥。在方法A中打印當前類名赦肃,然后多次 push進入UITableViewCell B中后再次進入 UITableViewCell A中,觸發(fā)通知A公浪,調(diào)用方法A會出現(xiàn)下面的情況:
跟我們猜想的一樣他宛,由于很多不同的UITableViewCell B 被創(chuàng)建,(都注冊了倆通知欠气,由于繼承的關(guān)系厅各,雖然UITableViewCell B 中沒有寫 UITableViewCell A的一些方法,但是UITableViewCell B的方法列表中還是會有 那些方法预柒,只是省去了書寫而已队塘,書寫在了父類文件中)而且沒有被銷毀,所以當UITableViewCell A 中的通知A被觸發(fā)時宜鸯,同樣的 UITableViewCell B 中的通知A 也被觸發(fā)憔古,由于UITableViewCell B 中沒有方法A,于是就去執(zhí)行了 父類(UITableViewCell A)中的方法A淋袖,于是就出現(xiàn)了 上圖那樣的場景鸿市。
解決辦法
單純避免崩潰的話,在UITableViewCell B中第一個 空的方法A 即可,或者把方法B 和 方法A 修改為不同即可焰情。
可是這樣陌凳,UITableViewCell A中的方法A依然會被執(zhí)行很多次。
#最后一個參數(shù)是表示會對哪個發(fā)送者對象發(fā)出的事件作出響應(yīng)内舟,nil 時表示接受所有發(fā)送者的事件合敦。,
#所以我們這里把 object:self ,即可只接受自己觸發(fā)的通知验游,而不會接受到其它 UITableViewCell觸發(fā)的通知了
#添加之前先移除所有監(jiān)聽充岛,可以解決多次注冊相同監(jiān)聽的問題。
[[NSNotificationCenter defaultCenter]removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(goodtongzhi:) name:@"goodPopAlert" object:self];
- (void)goodtongzhi :(NSNotification *)sender
{
#如果不是自己觸發(fā)的通知就不處理批狱。
if (![sender.object isEqual:self]) {
return;
}
NSDictionary *dataDic = sender.userInfo;
NSLog(@"你好我是 %@",self.class);
if ([dataDic[@"index"] integerValue] == self.tag) {
goods =dataDic[@"data"];
rightTextLablel[0].text = goods.name;
rightTextLablel[2].text = goods.danwei;
baozhiqiTexttF.text = goods.baizhiqi;
rightTextLablel[4].text = goods.baizhiqidanwei;
}
}
小結(jié)
雖然我們解決了崩潰裸准,也解決了方法的多次調(diào)用,看似達到了要求赔硫,其實在 UITableViewCell中注冊通知是很不好的方法,這樣會造成很多 UITableViewCell 無法被釋放盐肃,一直在內(nèi)存中爪膊,使用 多層次的Block回調(diào),一樣可以達到通知的效果砸王,而且不會造成UITableViewCell無法被釋放的問題推盛,本文詳細分析這個問題,旨在希望大家寫程序時注意這個問題谦铃。