如何在dealloc方法中引用__weak self

时间:2013-11-11 13:30:33

标签: ios objective-c automatic-ref-counting weak-references dealloc

我在各个地方调用了一个名为“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

错误发生在第二行

5 个答案:

答案 0 :(得分:16)

只是为了让“你不应该”“你不能”其他好的答案的一部分 更精确:

用于存储弱引用的运行时函数是objc_storeWeak(),并且 Clang/ARC documentation州:

  

id objc_storeWeak(id *object, id value);

     

...   如果value是空指针或它指向的对象已经开始   deallocation,对象被指定为null并且未注册为__weak   宾语。否则,对象被注册为__weak对象或具有它   注册更新以指向价值。

由于self对象已经开始取消分配,weakSelf应设置为NULL (因此没有任何用处)。

然而,似乎有一个错误(如此处所讨论http://www.cocoabuilder.com/archive/cocoa/312530-cannot-form-weak-reference-to.htmlobjc_storeWeak()在这种情况下崩溃,而不是返回NULL

答案 1 :(得分:4)

如果对象处于dealloc状态,则不应该为其创建任何新引用。考虑已经销毁的对象。不要再在回调/委托中使用它了。

请注意,dlcounter将永远不会被阅读。只需取消连接而不读取结果。

TL; DR
- 如何在__weak方法中引用dealloc个自我? - 不要参考它。

答案 2 :(得分:4)

你不能在dealloc方法中初始化一周(或强)对self的引用,并在别处使用它 - 它太晚了,对象将不可避免地被破坏。

但是,你可以试试这个:

-(void)dealloc
{
    NSArray* localDownloads = self.downloads;
    for(Download* dl in localDownloads) {
        [dl cancel];
    }
}

应该很清楚,有更好的地方可以调用取消,例如,在视图控制器中,您可以覆盖viewWillDisappear:

答案 3 :(得分:0)

我假设你正在为你的项目使用ARC。

直接来自Apple: Apple Talked about Weak and Strong

__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: Dealloc Method Explained and More

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.

在指出之后......我强烈建议您修改代码设计,因为您没有理由调用弱类型(自我)来解决您取消这些下载的问题dealloc或任何类型的deallocing涉及_ _typeof__self。

我可以推荐的是,您尝试取消这些下载的课程,使其通过下载UniqueID跟踪这些下载,并在dealloc中停止或删除它们。它更简单,更容易管理,而不是那种对__weak self和你正在做的所有代码的奇怪调用。

答案 4 :(得分:0)

简而言之:您可以在__strong而不是dealloc中使用__weak自我引用,但是当且仅当强引用不会超过dealloc时{1}}。否则,我会建议使用__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的引用计数。此时ViewController已经被释放并已经释放。 但是,恕我直言,“最安全”的方法是在这种情况下使用unsafe_unretained。从技术上讲,它与使用assign相同:无论内存管理如何,都将分配指针,并且当超出范围时不需要释放引用。但是使用unsafe_unretained告诉读者您的代码(或未来的您)您已经意识到风险,并且必须有理由去做您所做的事情。