我正在开发一个应用程序,在该应用程序中,向导航控制器推送和弹出的不同视图控制器可以注册 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];
据我所理解:
- 我正在视图控制器中创建一个堆栈块
- 然后将其传递给单例,后者创建它的堆副本,将其存储在可变字典中并调用 release 以平衡块上增加的保留计数。
- 一旦声明它的视图控制器的方法超出范围,原始堆栈块应该不再存在。
- 我什至没有从块的封闭范围中引用任何东西,所以它不应该保留 self 或任何变量/对象。它在执行时从参数中获取它的东西。
所以我不知道泄漏在哪里。此外,当视图控制器被弹出时,我会从 animationBlocks 数组中删除块,但我什至不需要这个(除了在我完成后立即回收内存)。因为下次用户推动同一个视图控制器时,它将使用相同的键重新注册动画块,并且使用现有键调用 setObject:withKey 将导致释放被发送到散列到该键的对象然后将新对象设置到该位置。
我在看什么?