我有一个在不同的地方调用的方法,称为"cancelAllPendingDownloads"这是取消各种作业并更新内部计数器的常规方法。
在 dealloc 方法中调用它时出现问题
-(void)dealloc
{
[self cancelAllPendingDownloads]; // want to cancel some jobs
}
-(void)cancelAllPendingDownloads // updates some internals
{
__weak __typeof__(self) weakSelf = self; // This line gets a EXC_BAD_INSTRUCTION error in runtime
for(Download *dl in self.downloads)
{
dl.completionHandler = ^{ // want to replace the previous block
weakSelf.dlcounter--;
}
[dl cancel];
}
}
不知道为什么它在 dealloc 方法中失败,因为"self"仍然存在
当我将代码更改为
__typeof__(self) strongSelf = self; //everything works fine
__weak __typeof__(self) weakSelf = strongSelf; (or "self") BAD_INSTRUCTION error
错误发生在第二行
只是为了让"你不应该"或"你不能"成为其他好答案的一部分更精确:
用于存储弱引用的运行时函数是 objc_storeWeak()
,并且Clang/ARC 文档指出:
id objc_storeWeak(id *object, id value);
。 如果值是空指针或它指向的对象已开始 解除分配,对象被分配为 null 并取消注册为__weak 对象。否则,对象将注册为__weak对象或具有其 注册已更新为指向值。
由于self
对象已经开始释放,因此应将weakSelf
设置为 NULL
(因此没有任何用处)。
但是,似乎有一个错误(如此处所述 http://www.cocoabuilder.com/archive/cocoa/312530-cannot-form-weak-reference-to.html)在这种情况下,objc_storeWeak()
崩溃,而不是返回NULL
.
如果对象处于dealloc
状态,则不应创建对它的任何新引用。将对象视为已销毁。不要再在回调/委托中使用它。
请注意,dlcounter
永远不会被读取。只需取消连接而不读取结果。
博士- 如何在dealloc
方法中引用__weak
自我?
- 不要引用它。
你不能在 dealloc 方法中初始化一周(或强)对 self 的引用并在其他地方使用它 - 为时已晚,对象将不可避免地被破坏。
但是,您可以尝试以下操作:
-(void)dealloc
{
NSArray* localDownloads = self.downloads;
for(Download* dl in localDownloads) {
[dl cancel];
}
}
应该清楚的是,有更好的位置来调用取消,例如,在视图控制器中,您可以覆盖viewWillDisappear:
。
我假设您正在为您的项目使用 ARC。
直接来自苹果:苹果谈弱与强
__strong is the default. An object remains “alive” as long as
there is a strong pointer to it.
__weak specifies a reference that does not keep the referenced object alive.
A weak reference is set to nil when there are no strong references to the object.
这是一篇解释 Dealloc 的文章:解位方法解释及更多
This method will be called after the final release of the object
but before it is deallocated or any of its instance variables are destroyed.
The superclass’s implementation of dealloc will be called automatically when
the method returns.
在指出这一点之后...我强烈建议您修改代码设计,因为您没有理由调用弱类型(self)来解决在 dealloc 上取消这些下载的问题或涉及 _weak_typeof__self 的任何类型的释放。
不过,我可以建议的是,您尝试取消这些下载的那个类,让它使用下载唯一ID跟踪这些下载,然后停止它们或在dealloc中删除它们。它更简单,更容易管理,而不是对__weak自我和你正在做的所有代码的奇怪调用。
简而言之:您可以在dealloc
中使用对 self 的__strong
引用,而不是出于您的目的使用 __weak
,但前提是且仅当该强引用不会超过 dealloc
的结束。否则,我建议使用 __unsafe_unretained
,如果它的寿命超过dealloc
,但阅读起来更清晰,它仍然是不安全的。
更长:我遇到过类似的情况,即 dealloc 期间的对象(视图控制器)应该取消订阅通知。这是一个自定义通知系统,取消订阅需要创建一个对象,其中包含对正在取消订阅的实体的引用。我最终遇到了同样的情况:在 dealloc 中,无法创建该对象,因为它需要一个导致崩溃的弱引用(这是一些愚蠢的演示代码,而不是您在生产中会拥有的代码):
@interface Dummy : NSObject
@property(nonatomic, weak) id weakProperty;
@property(nonatomic, strong) id strongProperty;
@property(nonatomic, unsafe_unretained) id unsafeProperty;
- (instancetype)initWithWeakStuff:(id)stuff;
- (instancetype)initWithStrongStuff:(id)stuff;
- (instancetype)initWithUnsafeStuff:(id)stuff;
@end
@implementation Dummy
- (instancetype)initWithWeakStuff:(id)stuff {
self = [super init];
if (self) {
_weakProperty = stuff;
}
return self;
}
- (instancetype)initWithStrongStuff:(id)stuff {
self = [super init];
if (self) {
_strongProperty = stuff;
}
return self;
}
- (instancetype)initWithUnsafeStuff:(id)stuff {
self = [super init];
if (self) {
_unsafeProperty = stuff;
}
return self;
}
- (void)dealloc {
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)dealloc {
Dummy *dummy = [[Dummy alloc] initWithStrongStuff:self];
[[NSNotificationCenter defaultCenter]
postNotificationName:@"some notification"
object:dummy]; // do something with it
}
@end
另一方面,如果参考很强,那么一切似乎都运行良好(在 dealloc 期间)。如果新创建的对象比自身寿命长,则会出现以下问题:
- (void)dealloc {
Dummy *dummy = [[Dummy alloc] initWithStrongStuff:self];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter]
postNotificationName:@"some notification"
object:dummy]; // do something with it
}); //Crash at the end of the block during dummy's dealloc
}
这意味着每当dummy
对象需要 dealloc 时,它都会尝试减少其strongProperty
的 ref 计数。在这一点上,ViewController
已经被释放和释放。但是,恕我直言,在这种情况下使用unsafe_unretained
"最安全"的方法是使用。从技术上讲,这与使用 asassign 相同:无论内存管理如何,都将分配指针,并且当该引用超出范围时不需要释放该引用。但是使用unsafe_unretained
告诉代码的读者(或未来的你),你已经意识到了风险,并且一定有理由做你所做的事情。