[英]Weird EXC_BAD_ACCESS crash related with setter/getter in iOS
有人可以解釋為什么跟隨我的代碼崩潰嗎? 在foo方法中的塊內發生崩潰。 我有EXC_BAD_ACCESS或“對象錯誤:雙重釋放”。 當我將“啟用僵屍對象”設置為“開”時,我還收到了“-[NSObject description]:消息發送到已釋放實例”。
@interface ViewController ()
@property (nonatomic, strong) NSObject *obj;
@end
@implementation ViewController
// just adding button
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
[btn setTitle:@"test" forState:UIControlStateNormal];
btn.frame = CGRectMake(100, 100, 100, 100);
[btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
// fired by button
- (void)btnAction:(id)sender {
for (int i = 0; i < 100; i++) {
[self foo];
}
}
// I want to understand this method
- (void)foo {
NSLog(@"foo");
self.obj = NSObject.new;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"%@", [[self obj] description]); // sometimes crash happenes here with a message "-[NSObject description]: message sent to deallocated instance"
});
}
@end
看起來self.obj被釋放在[self obj]和[obj description]之間。 但是我不確定為什么。
我認為[self obj]中的對象應該屬於它的作用域,即使在其他線程上同時執行self.obj = NSObject.new也不應該將其釋放。 我的理解錯了嗎?
我正在使用ARC在iOS 7.0.4上進行測試。 謝謝!
您有一個for循環正在調用-foo
方法,因此self.obj迅速設置為新值。 每次發生這種情況時,您都在異步執行代碼,這些代碼正在訪問您的( nonatomic
)屬性。 但是,即使在從多個線程訪問該屬性時總是獲得該屬性的正確值,主線程也很有可能在后台線程使用該屬性的先前值完成操作之前,將該屬性設置為新值。 一旦將屬性更改為新值,它就會釋放分配給它的先前對象。
由於您要從多個線程訪問屬性,因此您希望它是原子的而不是非原子的,因此請將屬性更改為:
@property (strong) NSObject *obj;
默認是atomic
。 對異步塊執行以下操作可能也更安全:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSObject *obj = self.obj;
if (self.obj) {
NSLog(@"%@", [obj description]);
}
});
如果這樣做,您將不再看到崩潰,因為obj
在塊內將始終為nil
或對它有強引用的有效對象。
但是,您可能不會獲得期望的結果。 對於異步塊的每次執行,都不能保證會得到要創建的NSObject
的后續實例。 有時候,它執行您的塊時, obj
兩次都是相同的對象,並且您永遠看不到某些已創建的對象。 這是因為異步塊不會在調用調用塊之前立即獲取實例集,而是從屬性中獲取實例集。 如果希望它立即使用實例集,則必須執行以下操作:
__block NSObject *obj = NSObject.new;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"%@", [obj description]);
});
這應該始終使用為異步塊調用專門創建的實例。
我懷疑問題是由nonatomic
屬性屬性引起的,因為您正在重新分配self.obj
100次,我認為后台線程有可能讀取部分重新分配的對象指針。
請嘗試:
@property (atomic, strong) NSObject *obj;
到完成后台日志記錄時, self.obj
可能已經不同或正在更改中。
使用這樣的局部變量:
- (void)foo {
NSLog(@"foo");
NSObject *val = [NSObject new];
self.obj = val;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"%@", val);
});
}
這將避免線程問題,並確保NSLog
記錄正確的實例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.