tai*_*ino 0 memory-management objective-c ios automatic-ref-counting
有人可以解释为什么跟随我的代码崩溃吗?在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
Run Code Online (Sandbox Code Playgroud)
看起来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;
Run Code Online (Sandbox Code Playgroud)
atomic
是默认值。对异步块执行以下操作可能也更安全:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSObject *obj = self.obj;
if (self.obj) {
NSLog(@"%@", [obj description]);
}
});
Run Code Online (Sandbox Code Playgroud)
如果执行此操作,您将不再看到崩溃,因为在块内obj
它将始终是nil
或有效引用了该对象的有效对象。
但是,您可能不会获得期望的结果。对于异步块的每次执行,都不能保证会得到NSObject
要创建的后续实例。有时它执行您的块时,obj
两次都是相同的对象,并且有时您从不会看到某些已创建的对象。这是因为异步块在调用该块之前没有立即获取实例集,而是从属性中获取实例集。如果要使它立即使用实例集,则必须执行以下操作:
__block NSObject *obj = NSObject.new;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"%@", [obj description]);
});
Run Code Online (Sandbox Code Playgroud)
这应该始终使用为异步块的调用专门创建的实例。