簡體   English   中英

與iOS中的setter / getter相關的EXC_BAD_ACCESS奇怪的崩潰

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM