从 dealloc 内部间接访问弱 ivar 时如何避免 EXC_BAD_ACCESS 错误
How to avoid EXC_BAD_ACCESS error when indirectly accessing weak ivar from inside dealloc
我有一个包装 C++ 组件的 Objective-C 层。当前的设计是为了健壮,这意味着用户可以随时将实例设置为 nil (ARC),并且底层组件将正确地同步清理自身。
我遇到的问题是顶部的 Obj-C 实例将自身作为 __weak 引用传递给底层 C++ 层,以便在操作期间访问(例如调用委托、更改某些状态等)当用户通过将 Obj-C 实例设置为 nil 来释放它时,当试图从 dealloc 内部访问它时会发生 EXC_BAD_ACCESS。
下面是一些示例代码,仅用于演示有问题的场景。
Wrapper.h
#import <Foundation/Foundation.h>
#import "Underlying.h"
@interface Wrapper : NSObject {
Underlying* _cppUnderlying;
}
- (instancetype)init;
- (void)dealloc;
@end
包装器+Private.h
#import "Wrapper.h"
@interface Wrapper () {
@package
NSMutableDictionary* _dict;
}
@end
Wrapper.mm
#import "Wrapper+Private.h"
@implementation Wrapper
- (instancetype)init
{
self = [super init];
_cppUnderlying = new Underlying(self);
_dict = [NSMutableDictionary dictionary];
return self;
}
- (void)dealloc
{
delete _cppUnderlying;
}
@end
Underlying.h
@class Wrapper;
class Underlying
{
public:
Underlying(Wrapper* wrapper);
~Underlying();
private:
__weak Wrapper* _wrapper;
};
Underlying.mm
#include "Underlying.h"
#import "Wrapper+Private.h"
Underlying::Underlying(Wrapper* wrapper) :
_wrapper(wrapper)
{
}
Underlying::~Underlying()
{
// ERROR OCCURS HERE.
[_wrapper->_dict setValue:@"value1" forKey:@"key1"];
}
最初,问题是 "Why the error?!" 但后来,我才发现:Weak property is set to nil in dealloc but property's ivar is not nil 其中包括详细解释(基本上,objc_loadWeak() returns dealloc 启动后立即为 nil)。
现在,问题是:
我可以采用什么样的 Obj-C 设计实践来完全避免这种情况? C++ 层在其析构函数中同步处理所有会话清理(如果有的话)。在 Objective-C 中似乎无法做到同样的事情。我是否应该提供 'release' 或 'close' 方法在允许用户释放实例之前异步执行所有清理工作?
谢谢!
在 -[Wrapper dealloc]
中怎么样,在你做 delete _cppUnderlying;
之前,你首先调用类似 _cppUnderlying->cleanup(_dict);
的东西,你明确地传递你的字典,甚至 _cppUnderlying->cleanup(self);
你传递的地方整个对象,让 Underlying
负责那里的所有清理工作?
您正在使用 ->
运算符取消引用 nil
:
[_wrapper->_dict setValue:@"value1" forKey:@"key1"];
要么测试 _wrapper
为 nil,要么将 make dict
更改为 属性,您可以通过点符号访问:
if (_wrapper) {
[_wrapper->_dict setValue:@"value1" forKey:@"key1"];
}
或
[_wrapper.dict setValue:@"value1" forKey:@"key1"];
我有一个包装 C++ 组件的 Objective-C 层。当前的设计是为了健壮,这意味着用户可以随时将实例设置为 nil (ARC),并且底层组件将正确地同步清理自身。
我遇到的问题是顶部的 Obj-C 实例将自身作为 __weak 引用传递给底层 C++ 层,以便在操作期间访问(例如调用委托、更改某些状态等)当用户通过将 Obj-C 实例设置为 nil 来释放它时,当试图从 dealloc 内部访问它时会发生 EXC_BAD_ACCESS。
下面是一些示例代码,仅用于演示有问题的场景。
Wrapper.h
#import <Foundation/Foundation.h>
#import "Underlying.h"
@interface Wrapper : NSObject {
Underlying* _cppUnderlying;
}
- (instancetype)init;
- (void)dealloc;
@end
包装器+Private.h
#import "Wrapper.h"
@interface Wrapper () {
@package
NSMutableDictionary* _dict;
}
@end
Wrapper.mm
#import "Wrapper+Private.h"
@implementation Wrapper
- (instancetype)init
{
self = [super init];
_cppUnderlying = new Underlying(self);
_dict = [NSMutableDictionary dictionary];
return self;
}
- (void)dealloc
{
delete _cppUnderlying;
}
@end
Underlying.h
@class Wrapper;
class Underlying
{
public:
Underlying(Wrapper* wrapper);
~Underlying();
private:
__weak Wrapper* _wrapper;
};
Underlying.mm
#include "Underlying.h"
#import "Wrapper+Private.h"
Underlying::Underlying(Wrapper* wrapper) :
_wrapper(wrapper)
{
}
Underlying::~Underlying()
{
// ERROR OCCURS HERE.
[_wrapper->_dict setValue:@"value1" forKey:@"key1"];
}
最初,问题是 "Why the error?!" 但后来,我才发现:Weak property is set to nil in dealloc but property's ivar is not nil 其中包括详细解释(基本上,objc_loadWeak() returns dealloc 启动后立即为 nil)。
现在,问题是: 我可以采用什么样的 Obj-C 设计实践来完全避免这种情况? C++ 层在其析构函数中同步处理所有会话清理(如果有的话)。在 Objective-C 中似乎无法做到同样的事情。我是否应该提供 'release' 或 'close' 方法在允许用户释放实例之前异步执行所有清理工作?
谢谢!
在 -[Wrapper dealloc]
中怎么样,在你做 delete _cppUnderlying;
之前,你首先调用类似 _cppUnderlying->cleanup(_dict);
的东西,你明确地传递你的字典,甚至 _cppUnderlying->cleanup(self);
你传递的地方整个对象,让 Underlying
负责那里的所有清理工作?
您正在使用 ->
运算符取消引用 nil
:
[_wrapper->_dict setValue:@"value1" forKey:@"key1"];
要么测试 _wrapper
为 nil,要么将 make dict
更改为 属性,您可以通过点符号访问:
if (_wrapper) {
[_wrapper->_dict setValue:@"value1" forKey:@"key1"];
}
或
[_wrapper.dict setValue:@"value1" forKey:@"key1"];