9

我有一个在各个地方调用的方法,称为“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

错误发生在第二行

4

5 回答 5

16

只是为了使其他好的答案中的“你不应该”“你不能”部分更准确:

用于存储弱引用的运行时函数是objc_storeWeak()Clang/ARC 文档指出:

id objc_storeWeak(id *object, id value);

...如果 value 是空指针或它指向的对象已开始释放,则将 object 分配为 null 并取消注册为 __weak 对象。否则,对象将注册为 __weak 对象或更新其注册以指向值。

由于self对象已经开始释放,weakSelf应该设置为NULL (因此没有任何用处)。

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

于 2013-11-11T13:56:06.810 回答
4

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

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

TL;DR
- 我如何__weak在方法中引用 self dealloc
- 不要引用它。

于 2013-11-11T13:43:20.647 回答
4

您不能在 dealloc 方法中初始化一周(或强)对 self 的引用并在其他地方使用它 - 为时已晚,对象将不可避免地被破坏。

但是,你可以试试这个:

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

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

于 2013-11-11T13:43:23.013 回答
0

我假设您正在为您的项目使用 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 的文章: Dealloc Method Explained 等

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.

在这被指出之后......我强烈建议您修改您的代码设计,因为您没有理由调用typeof (self) 来解决您在 dealloc 或任何类型的涉及 _weak 的取消分配时取消这些下载的问题_typeof__self 就此而言。

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

于 2013-11-11T14:45:04.033 回答
0

简而言之:您可以使用__strong对 self in 的引用dealloc而不是__weak出于您的目的,但当且仅当该强引用不会超过dealloc. 否则,我建议使用__unsafe_unretained,如果它dealloc

Longer:我遇到过类似的情况,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 期间)。如果新创建的对象比 self 寿命更长,就会出现问题:

- (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对象需要解除分配时,它都会尝试减少其strongProperty. 到那时,它ViewController已经被释放并释放了。但是,恕我直言,“最安全”的继续方式是unsafe_unretained在这种情况下使用。从技术上讲,它与使用分配相同:无论内存管理如何,都会分配指针,并且当超出范围时不需要释放该引用。但是 usingunsafe_unretained告诉你的代码的读者(或未来的你)你意识到了风险,并且一定有理由去做你所做的事情。

于 2015-04-23T17:33:45.140 回答