有人可以解释为什么我的代码崩溃之后吗?崩溃发生在FOO方法中的块内。我有exc_bad_access或"对象错误:双免费"。我还得到了" - [nsobject Description]:发送到Deallocated实例的消息"启用Zombie Objects"。
@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]和[obj描述]之间是self.obj。但是我不确定为什么。
我认为,来自[self obj]的对象应由其范围所有,即使self.obj = nsobject.new在其他线程上同时执行。我的理解错了吗?
我正在使用ARC测试iOS 7.0.4。谢谢!
您有一个正在调用 -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次我认为有可能背景线程读取部分重新分配的对象指针。p>请尝试:
@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
记录适当的实例。