場景
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的關系和方法的調用關系大致如此,崩潰的原因是,由于 方法B和方法A是一樣的,UITableViewCell B 繼承與 UITableViewCell A,由于

UITableViewCell B在初始化的時候調用了 UITableViewCell A中的初始化方法,所以由于繼承的機制,實際上 UITableViewCell B注冊了兩個通知:通知A和通知B。由于方法B和方法A是一樣的,所以UITableViewCell B中的通知A調用方法A的時候,實際上就調用了方法B,(當子類的方法列表中有和父類的方法列表中的方法一樣的情況下,會調用子類中的方法,而不調用父類中的方法,也就是重寫),而實際上 UITableViewCell B 中的方法B設計上不是為 通知A服務的,其中調用的一些未知的數(shù)據(jù),所有就出現(xiàn)了崩潰。
有一個問題:為什么從 UITableViewCell B中POP出后,UITableViewCell B沒有被釋放呢?,就是因為UITableViewCell B沒有在頁面被 POP后被釋放掉,才會出現(xiàn)這樣的 Crash,那么為什么沒被釋放呢
dealloc的不被調用的情況。
ARC下,控制器在被pop后移出棧后會被釋放,但有些時候會發(fā)現(xiàn)控制器出棧的時候不會調用dealloc方法,系統(tǒng)可以幫我們釋放該對象,及其包含的對象;但是卻無法釋放不屬于該對象的一些東西,就造成了 對象的dealloc方法不被調用。而且重寫該方法時不能顯式調用[super dealloc],和繼承中先加載父類再加載子類相反,注銷時先注銷子類之后再注銷父類。因為系統(tǒng)會自動幫你調用父類的dealloc方法。
- 1.通知的觀察者,或KVO的觀察者
由于通知中心是系統(tǒng)的一個單例,你在注冊通知的觀察者時,實際上是在通知中心注冊的,
這時,即使ARC下系統(tǒng)幫我們釋放了對象,但是在通知中心的觀察還是沒有移除,那么當有
該通知時,依然會嘗試調用該對象的接受通知的方法,這可能會導致一些問題.
- 2.對象強委托
對于其他的對象來把你當做委托 delegate時,并且是 強引用時,即時你自身被釋放,但是引用你的對象依然還在,這時需要在引用你的對象移除該delegate
- 3.一些其它的資源,類似地圖頁面。C語言寫的一些好內存的類文件,
- 4.控制器中NSTimer沒有被銷毀
當viewController中存在NSTimer時,需要特別注意,當調用
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES]時,因為 target:self ,也就是引用了當前viewController,導致控制器的引用計數(shù)加1,如果沒有將這個NSTimer 銷毀,它將一直保留該viewController,無法釋放,也就不會調用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 都不會被釋放,而是一直在內存中。
驗證猜想
我們修改 方法B 使方法A和 方法B不一樣。在方法A中打印當前類名,然后多次 push進入UITableViewCell B中后再次進入 UITableViewCell A中,觸發(fā)通知A,調用方法A會出現(xiàn)下面的情況:

跟我們猜想的一樣,由于很多不同的UITableViewCell B 被創(chuàng)建,(都注冊了倆通知,由于繼承的關系,雖然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ā)出的事件作出響應,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;
}
}
小結
雖然我們解決了崩潰,也解決了方法的多次調用,看似達到了要求,其實在 UITableViewCell中注冊通知是很不好的方法,這樣會造成很多 UITableViewCell 無法被釋放,一直在內存中,使用 多層次的Block回調,一樣可以達到通知的效果,而且不會造成UITableViewCell無法被釋放的問題,本文詳細分析這個問題,旨在希望大家寫程序時注意這個問題。