0

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

Instruments 报告说,每次我推动控制器时,我都会泄漏块对象(每次推动时,每个动画块都会泄漏 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"];
}

根据 Instruments 的说法,我每次都会泄漏:

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

从第一个代码片段开始,它转换为:

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

据我所理解:

  1. 我正在视图控制器中创建一个堆栈块
  2. 然后将其传递给单例,后者创建它的堆副本,将其存储在可变字典中并调用 release 以平衡块上增加的保留计数。
  3. 一旦声明它的视图控制器的方法超出范围,原始堆栈块应该不再存在。
  4. 我什至没有从块的封闭范围中引用任何东西,所以它不应该保留 self 或任何变量/对象。它在执行时从参数中获取它的东西。

所以我不知道泄漏在哪里。此外,当视图控制器被弹出时,我会从 animationBlocks 数组中删除块,但我什至不需要这个(除了在我完成后立即回收内存)。因为下次用户推动同一个视图控制器时,它将使用相同的键重新注册动画块,并且使用现有键调用 setObject:withKey 将导致释放被发送到散列到该键的对象然后将新对象设置到该位置。

我在看什么?

4

1 回答 1

3

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

animationBlock = [animationBlock copy];
[self.animationBlocks setObject:animationBlock forKey:key];
[animationBlock release];
于 2012-05-11T05:36:27.853 回答