1

[編集: 混乱を招いていたため、このケース全体では ARC ではなく MRR を想定しています]

自分自身を (間接的に) 参照し、別のオブジェクトのプロパティにコピーされる (つまり、オブジェクト As のスタックからコピーされる) ブロックで奇妙な動作をしています (明らかに説明がありますが、それを理解することはできません)。ヒープに保存され、オブジェクトによって保持されます B)。ブロックに _this への参照が含まれていない場合、オブジェクト A の dealloc は、ナビゲーション コントローラーからポップされるたびに呼び出されます。ただし、ブロックが _this を参照する場合、オブジェクト (以下のコードでは MyObjectA) の dealloc は 1 回おきにしか呼び出されません。つまり、このビュー コントローラー サブクラスをプッシュし、createBlock を呼び出し、ポップしても何も起こりません。私はもう一度プッシュし、createBlock が再び呼び出され、次にポップし、MyObjectA で dealloc を呼び出します。

簡潔にするために、動作の鍵と思われるスニペットのみを掲載しています。

次のように、createBlock メソッドを含むオブジェクト MyObjectA (カスタム UIViewController のサブクラス) があるとします。

- (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.viewへの弱い(別名__blockクラス*識別子= eval)参照を取得することです。これが、自分自身ではなく、自分自身を最初に参照していた理由です。このように、このビューの参照カウントが増加しているにもかかわらず、自己の参照カウントは正しいままです。次に、ポップ時に、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 に答える