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"];

上記のメソッドは、たとえばViewControllerの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. 元のスタックブロックは、それが宣言されたView Controllerのメソッドがスコープ外になると、存在しなくなるはずです。
  4. 私はブロックの囲んでいるスコープから何も参照していないので、自分自身や変数/オブジェクトを保持するべきではありません。実行時にパラメータから処理するものを取得します。

だから私はリークがどこにあるのかわかりません。さらに、View Controllerがポップされたときに、animationBlocks配列からブロックを削除していますが、これは必要ありません(使い終わったらすぐにメモリを再利用する以外)。次回ユーザーが同じViewControllerをプッシュするようにすると、アニメーションブロックが同じキーで再登録され、既存のキーでsetObject:withKeyを呼び出すと、そのキーにハッシュされたオブジェクトにリリースが送信されます。次に、新しいオブジェクトをその場所に設定します。

私は何を見落としていますか?

4

1 に答える 1

3

元のブロックではなく、作成したコピーをリリースする必要があります。あなたがする必要があります:

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