仪器报告我正在泄漏NSMallocBlocks(又名块对象),但我真的吗?

时间:2012-05-10 19:33:47

标签: ios cocoa-touch instruments objective-c-blocks

我正在开发一个应用程序,其中可以向导航控制器推送和弹出的不同视图控制器可以注册CoreAnimation序列,然后在推送控制器的整个生命周期内的不同时间触发这些序列。

仪器报告说,每次推动控制器时,我都会泄漏块对象(每次推动时,对于每个动画块都会出现32字节的泄漏)。但是,我不知道我在哪里漏水。这是相关的代码:

有一个单独的AnimationFactory,其中包括这个方法:

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

然后,不同的视图控制器在推送到导航控制器的堆栈时,使用这样的代码注册不同的动画序列,例如:

- (void)setupCommonAnimations
{
  int(^animationBlock)(NSArray*,NSDictionary*);

  /************************************************************************************************************************/
  //  Move stuff up
  /************************************************************************************************************************/
  animationBlock =
  ^(NSArray* layers, NSDictionary* parameters)
  {
    CGFloat timeOffset = [[parameters objectForKey:@"timeOffset"] floatValue];
    CABasicAnimation* a;

    CABasicAnimation* a2 = [CABasicAnimation animationWithKeyPath:@"opacity"];
    a2.fromValue = [NSNumber numberWithFloat:0.];
    a2.toValue = [NSNumber numberWithFloat:1.];

    CAAnimationGroup* g = [CAAnimationGroup animation];
    g.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    g.fillMode = kCAFillModeBoth;
    g.removedOnCompletion = NO;
    g.duration = .4;

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

    for(CALayer *layer in layers)
    {
      a = [CABasicAnimation animationWithKeyPath:@"position.y"];
      a.fromValue = [NSNumber numberWithFloat:1024 + layer.frame.size.height / 2];
      a.toValue = [NSNumber numberWithFloat:layer.frame.origin.y + layer.frame.size.height / 2];

      g.animations = [NSArray arrayWithObjects:a,a2, nil];
      g.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] + timeOffset;

      [layer addAnimation:g forKey:nil];

      timeOffset += .2;
    }

    [CATransaction commit];

    return 0;
  };

  [[AnimationFactory sharedFactory] registerAnimationBlock:animationBlock forKey:@"StuffUpAnimation"];

/************************************************************************************************************************/
  //  Sequential Fade-in
  /************************************************************************************************************************/
  animationBlock =
  ^(NSArray* layers, NSDictionary* parameters)
  {
    CGFloat timeOffset = [[parameters objectForKey:@"timeOffset"] floatValue];
    CABasicAnimation* a2 = [CABasicAnimation animationWithKeyPath:@"opacity"];
    a2.fromValue = [NSNumber numberWithFloat:0.];
    a2.toValue = [NSNumber numberWithFloat:1.];
    a2.duration = .4;
    a2.fillMode = kCAFillModeBoth;
    a2.removedOnCompletion = NO;

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

    for(CALayer *layer in layers)
    {
      a2.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] + timeOffset;
      [layer addAnimation:a2 forKey:nil];
      timeOffset += .4;
    }

    [CATransaction commit];

    return 0;
  };

  [[AnimationFactory sharedFactory] registerAnimationBlock:animationBlock forKey:@"SeqFadeInAnimation"];

上面的方法将在视图控制器的init或viewWillAppear上调用。我正在重复使用animationBlock变量来注册不同的动画。

最后,当一个控制器被弹出时,它会调用以下内容作为其dealloc序列的一部分:

- (void)cleanupAnimations
{
  [[AnimationFactory sharedFactory] removeAnimationBlockForKey:@"SeqFadeInAnimation"];
  [[AnimationFactory sharedFactory] removeAnimationBlockForKey:@"StuffUpAnimation"];
}

根据乐器,我每次都会泄漏:

[[AnimationFactory sharedFactory] registerAnimationBlock:animationBlock forKey:@"StuffUpAnimation"];

,从第一个代码段转换为:

[self.animationBlocks setObject:[animationBlock copy] forKey:key];
[animationBlock release];

据我所知:

  1. 我正在视图控制器中创建堆栈块
  2. 然后将其传递给单例,后者创建它的堆副本,将其存储在可变字典中并调用release以平衡块上增加的保留计数。
  3. 一旦视图控制器声明它的方法超出范围,原始堆栈块就应该不再存在。
  4. 我甚至没有引用块的封闭范围中的任何内容,因此它不应该保留self或任何变量/对象。它在执行时从参数中获取它的东西。
  5. 所以我不知道泄漏在哪里。此外,我正在弹出视图控制器时从animationBlocks数组中删除块,但我甚至不需要这个(除了在我完成后回收内存)。因为下次用户导致相同的视图控制器被推送时,它将使用相同的密钥重新注册动画块,并且使用现有密钥调用setObject:withKey将导致将释放发送到散列到该密钥的对象然后将新对象设置为该位置。

    我在俯瞰什么?

1 个答案:

答案 0 :(得分:3)

您应该发布您制作的副本,而不是原始块。你需要这样做:

animationBlock = [animationBlock copy];
[self.animationBlocks setObject:animationBlock forKey:key];
[animationBlock release];