[编辑:由于引起混淆,整个案例假设MRR而不是ARC]
我有一个奇怪的(显然有一个解释,我只是无法弄清楚)一个引用自我(间接)的块的行为,并被转移到另一个对象的属性(即从对象复制)作为'堆栈到堆并由对象B保留)。如果块不包含对_this的引用,则每次从导航控制器弹出对象A的dealloc时都会调用它。但是,如果块引用_this,那么对象的(下面的代码中的MyObjectA)dealloc只会每隔一次调用一次。也就是说,我推这个视图控制器子类,调用createBlock,我弹出并没有任何反应。我再次推送,再次调用createBlock,然后弹出然后它在MyObjectA上调用dealloc。
为了简洁起见,我只发布我认为对行为至关重要的片段。
假设我有一个对象MyObjectA(自定义UIViewController的sublcass),它包含一个方法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中......但我不确定。 有什么想法吗?
答案 0 :(得分:0)
[更新:这个答案在使用ARC时适用,从评论中看出MRC正在使用,所以它不是答案!]
__block
属性适用于需要可由块更新的变量的情况,即变量通过引用而不是按值<传递给块 / em>这是默认情况。您的代码中似乎不需要这样做,您不会在块中更新_this
的值。
要打破强参考周期,请使用__weak
属性。您当前的_this
是对self
引用的同一对象的强引用,因此您的数据块会以强引用结束。
答案 1 :(得分:0)
好吧我明白了:当我将新复制的(堆)块添加到AnimationFactory的字典中时,即使在最初在自己声明块时执行弱引用shebang,我也必须保留自我。
解决方案是获取一个弱(也就是__block Class * identifier = eval,因为我不在ARC上)引用self.view,这就是我引用self开始的原因,而不是一个自我启动。这样,尽管此视图的引用计数增加,但自我引用计数保持正确。然后,在pop上,自动不会被AnimationFactory的字典保留并调用dealloc。
我应该提到self的dealloc包含对另一个方法的调用,该方法又从AnimationFactory中删除所有已注册的块,使self.view的保留计数过于隐含地恢复正常,从而不会泄漏。