1

[编辑:由于它引起了混乱,整个案例假设 MRR 而不是 ARC]

我有一个奇怪的行为(显然有一个解释,我只是想不通)一个块引用自我(间接)并被复制到另一个对象的属性(即,从对象 As' 堆栈复制到堆并由对象 B) 保留。如果该块不包含对 _this 的引用,则每次从导航控制器弹出对象 A 的 dealloc 时都会调用它,这是应该的。但是,如果块引用 _this,则对象的(下面代码中的 MyObjectA)dealloc 只会每隔一次调用一次。也就是说,我推送这个视图控制器子类,调用 createBlock,然后弹出并没有任何反应。我再次推送,再次调用 createBlock,然后弹出,然后它确实在 MyObjectA 上调用 dealloc。

为简洁起见,我只发布我认为是行为关键的片段。

假设我有一个对象 MyObjectA(自定义 UIViewController 的子类),其中包括一个方法 createBlock,如下所示:

- (void)createBlock
{
  __block MyObjectA* _this = self;
  int(^animationBlock)(NSArray*,NSDictionary*);


  animationBlock =
  ^(NSArray* layers, NSDictionary* parameters)
  {
    [CATransaction begin];
    [CATransaction setCompletionBlock:
     ^{
       for(CALayer* layer in layers)
         layer.opacity = 1;
     }];

    CABasicAnimation* a2 = [CABasicAnimation animationWithKeyPath:@"opacity"];
    a2.fromValue = [NSNumber numberWithFloat:0.];
    a2.toValue = [NSNumber numberWithFloat:1.];
    a2.duration = .4;
    a2.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    a2.fillMode = kCAFillModeBoth;
    a2.removedOnCompletion = NO;

    CABasicAnimation* a = [CABasicAnimation animationWithKeyPath:@"position.x"];
    a.duration = .4;
    a.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    a.fillMode = kCAFillModeBoth;
    a.removedOnCompletion = NO;

    CAAnimationGroup* g = [CAAnimationGroup animation];
    g.animations = [NSArray arrayWithObjects:a,a2, nil];
    g.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    g.fillMode = kCAFillModeBoth;
    g.removedOnCompletion = NO;

    CALayer* numberLayer;
    CALayer* flechaLayer;
    CGFloat timeOffset = 0;
    for(int i = 0; i < [layers count]; i+=2)
    {
      numberLayer = [layers objectAtIndex:i];
      flechaLayer = [layers objectAtIndex:i+1];

      a2.beginTime = [_this.view.layer convertTime:CACurrentMediaTime() fromLayer:nil] + timeOffset;
      [numberLayer addAnimation:a2 forKey:nil];

      a2.beginTime = 0;
      a.fromValue = [NSNumber numberWithFloat:flechaLayer.frame.origin.x + 100];
      a.toValue = [NSNumber numberWithFloat:flechaLayer.frame.origin.x + flechaLayer.frame.size.width / 2.];
      g.duration = 3;
      g.beginTime = [_this.view.layer convertTime:CACurrentMediaTime() fromLayer:nil] + timeOffset + .4;
      [flechaLayer addAnimation:g forKey:nil];

      timeOffset += 1.5;
    }

    [CATransaction commit];

    return 0;
  };

  [[AnimationFactory sharedFactory] registerAnimationBlock:animationBlock forKey:@"EnsureFlechasNutricion"];
}

如您所见,动画块中有对 _this 的引用。然后,注册块的 AnimationFactory(单例)方法是:

- (void)registerAnimationBlock:(int(^)(NSArray*, NSDictionary*))animationBlock forKey:(NSString*)key
{
  int(^heapBlock)(NSArray*, NSDictionary*) = [animationBlock copy];
  [self.animationBlocks setObject:heapBlock forKey:key];
  [heapBlock release];
}

我的猜测是将块复制到堆中保留 MyObjectA,或者可能将块添加到 AnimationFactory 中的 NSMutableDictionary.. 但我不确定。有什么想法吗?

4

2 回答 2

0

好的,我想通了:当我将新复制的(堆)块添加到 AnimationFactory 的字典中时,即使在最初在 self.

解决方案是获取一个对 self.view 的弱引用(又名 __block Class* identifier = eval,因为我不在 ARC 上),这就是我开始引用 self 而不是 self 的原因。这样,尽管这个视图的引用计数增加,self 的引用计数保持正确。然后,在弹出时,self 不会被 AnimationFactory 的字典保留,而是会调用 dealloc。

我应该提到 self 的 dealloc 包括对另一个方法的调用,该方法反过来从 AnimationFactory 中删除所有已注册的块,从而使 self.view 的保留计数过于隐式地恢复正常,因此不会泄漏。

于 2012-10-03T04:43:03.620 回答
0

[更新:这个答案在使用 ARC 时适用,并且从评论中发现正在使用 MRC,所以它不是答案!]

__block属性用于当您需要一个可以由块更新的变量时,即变量通过引用而不是默认情况下的值传递给块。您的代码中似乎不需要这,您不会更新_this块内的值。

要打破强引用循环,请使用该__weak属性。您的 current_this是对引用的同一对象的强self引用,因此您的块以强引用结束。

于 2012-10-03T03:24:54.300 回答