ARC and UIAlertView: unrecognized selector sent to instance
將cell作為代理,當cell被釋放,出現的crash問題。
1.NSObject* obj = [[NSObject alloc] init];
控制臺,打印對象:
打印對象,會調用description方法,默認返回<類名: 地址>。
來自博客:iOS 常見 Crash 及解決方案
2.背景:
在自定義TableViewCell類中:
點擊cell上的某個按鈕,彈出AlertView。
點擊事件中,初始化這個alertView,調用alertView自定義初始化方法(initWithFrame:delegate:self),傳遞self即Cell,設置代理。
在AlertView類中:
聲明協議,并有三個協議方法如rightBtnClicked:等
使用前設置代理,在alertView,alloc的時候,設置的代理self.delegate = xxxCell
點擊如右邊的button,觸發事件
- (void)btnClicked:(UIButton *)btn?
{
? ? ? ? [self.delegate rightBtnClicked:btn] // crash,此時delegate已經被釋放了
}
實際上就是cell對象已經被釋放,即delegate被dealloc了。指針沒有置空,這時再訪問這個指針指向的內存,就會 Crash。
無法用if (nil = delegate)判斷delegate是否已經被dealloc掉,因為被dealloc之后,delegate對象也不是空的,這個時候delegate可能已經是野指針了。
3.解決辦法。
可以在cellforrow里判斷,在delegate是被釋放了的情況,重新賦值,設置代理。
調用者在對象銷毀時未將delegate置為nil,delegate將變成野指針,而導致程序掛掉。設置nil。
weak,assign。我們的delegate,在arc中用weak
當時解決問題參考的博文:
1.ios - ARC and UIAlertView: unrecognized selector sent to instance - Stack Overflow
2.ios 自動內存管理 ARC - Aimy的個人空間 - 開源中國社區
原文如下:
今天在公司爆出一個 BUG,導致5000+crash.
大致是 UIKit 中的 delegate 訪問了已經釋放了的界面,也就是使用了野指針導致 crash.
回來演示了一下發現
@property (nonatomic, assign) id delegate;//1
@property (nonatomic, weak) id delegate;//2
大部分的 UIKit 的 delegate 都是如1的聲明
因為 ios 在5之前是沒有 ARC 的,為了兼容所以寫的都是 assign
那么 assign 與 weak 有什么區別呢?
__strong NSString *yourString = [[NSString alloc] initWithUTF8String:"your string"];
__weak? NSString *myString = yourString;
yourString = nil;
__unsafe_unretained NSString *theirString = myString;
//現在所有的指針都為nil
weak的特性,如果指向的內存被釋放了,則自動指向 nil;
所以使用 weak 是不會有野指針的
而 assign 和unsafe_unretained,永遠指向某內存地址,如果該內存被釋放了,自己就會成為野指針
如下
__strong NSString *yourString = @"Your String";
__weak? NSString *myString = yourString;
__unsafe_unretained NSString *theirString = myString;
yourString = nil;
//現在yourString與myString的指針都為nil,而theirString不為nil,但是是野指針。
所以我們在使用 UIKit 中的 delegate 的時候,要避免響應 delegate 的VC,或者 View 之類的實例被提前釋放了,而導致 crash
而我們自己的 delegate 可以直接寫成 weak 的,既避免了循環引用,又不會產生野指針.
3.MRC下delegate 野指針問題 - James.Y - 博客園
在全局斷點中定位到出問題的點上,竟然是delegate回調的地方出現了問題!
if(self.delegate && [self.delegate respondsToSelector:@selector(test:)]) {
[self.delegate test:nil];
}