从 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"];