在沿着路径动画化视图的CAKeyframeAnimation中崩溃

4
我有一些代码可以对UIView进行动画处理:在路径上沿着运动的同时缩小视图。但是,它会崩溃并显示以下错误(可复现:总是如此):
CoreGraphics`CG::Path::apply(void*, void (*)(void*, CGPathElementType, CGPoint const*)) const:
0x1055c06:  pushl  %ebp
0x1055c07:  movl   %esp, %ebp
0x1055c09:  subl   $24, %esp
0x1055c0c:  movl   8(%ebp), %eax
0x1055c0f:  movl   (%eax), %ecx
0x1055c11:  movl   (%ecx), %eax
0x1055c13:  movl   16(%ebp), %edx
0x1055c16:  movl   %edx, 8(%esp)
0x1055c1a:  movl   12(%ebp), %edx
0x1055c1d:  movl   %edx, 4(%esp)
0x1055c21:  movl   %ecx, (%esp)
0x1055c24:  calll  *64(%eax)
0x1055c27:  addl   $24, %esp
0x1055c2a:  popl   %ebp
0x1055c2b:  ret    

我认为它来自下面的方法(pathAnimationWithStartPoint:),如果我不添加它返回的CAKeyframeAnimation,也就是只有:

[group setAnimations:@[ zoomAnimation ]];

取而代之

[group setAnimations:@[ zoomAnimation, pathAnimation ]];

它可以不崩溃地运行。

- (CAKeyframeAnimation *) pathAnimationWithStartPoint:(CGPoint) startPoint {
    CGPoint endPoint = self.showApplicationTourButton.center;

    CGFloat xDistanceBetweenStartAndEndPoints = abs(startPoint.x - endPoint.x);
    CGFloat yOfHighestPointOfCurvePath = endPoint.y - 250;

    CGPoint controlPoint1 =  CGPointMake(startPoint.x + 1.0f / 3.0f * xDistanceBetweenStartAndEndPoints, yOfHighestPointOfCurvePath);
    CGPoint controlPoint2 = CGPointMake(startPoint.x + 2.0f / 3.0f * xDistanceBetweenStartAndEndPoints, yOfHighestPointOfCurvePath);

    CGMutablePathRef curvedPath = CGPathCreateMutable();

    CGPathMoveToPoint(curvedPath, NULL, startPoint.x, startPoint.y);
    CGPathAddCurveToPoint(curvedPath, NULL, controlPoint1.x, controlPoint1.y, controlPoint2.x, controlPoint2.y, endPoint.x, endPoint.y);


    CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    pathAnimation.path = curvedPath;
    CGPathRelease(curvedPath);



    pathAnimation.calculationMode = kCAAnimationPaced;
    pathAnimation.fillMode = kCAFillModeForwards;
    pathAnimation.removedOnCompletion = NO;


    return pathAnimation;
}

回溯:

* thread #1: tid = 0x1a03, 0x01055c11 CoreGraphics`CG::Path::apply(void*, void (*)(void*, CGPathElementType, CGPoint const*)) const + 11, stop reason = EXC_BAD_ACCESS (code=2, address=0x0)
    frame #0: 0x01055c11 CoreGraphics`CG::Path::apply(void*, void (*)(void*, CGPathElementType, CGPoint const*)) const + 11
    frame #1: 0x00f5d090 CoreGraphics`CGPathApply + 64
    frame #2: 0x01838c68 QuartzCore`CA::Render::Path::new_path(CGPath const*, bool) + 158
    frame #3: 0x018c3493 QuartzCore`-[CAKeyframeAnimation _setCARenderAnimation:layer:] + 176
    frame #4: 0x018c3b2c QuartzCore`-[CAKeyframeAnimation _copyRenderAnimationForLayer:] + 68
    frame #5: 0x018c6f12 QuartzCore`-[CAAnimationGroup _copyRenderAnimationForLayer:] + 241
    frame #6: 0x018df547 QuartzCore`CA::Layer::commit_animations(CA::Transaction*, double (*)(CA::Layer*, double, void*), void (*)(CA::Layer*, CA::Render::Animation*, void*), void (*)(CA::Layer*, void**, void*), void*) + 641
    frame #7: 0x01855520 QuartzCore`CA::Context::commit_layer(CA::Layer*, unsigned int, unsigned int, void*) + 94
    frame #8: 0x018d87fa QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 330
    frame #9: 0x018d877e QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 206
    frame #10: 0x01856667 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 1775
    frame #11: 0x01857227 QuartzCore`CA::Transaction::commit() + 395
    frame #12: 0x018f9b17 QuartzCore`+[CATransaction commit] + 52
    frame #13: 0x000399bf MyApp`-[HomeViewController catchTourScrollViewScreenshotViewAndAnimateItToButton](self=0x0b4e5210, _cmd=0x006f381a) + 1903 at HomeViewController.m:197
    frame #14: 0x0391f663 libobjc.A.dylib`-[NSObject performSelector:] + 62
    frame #15: 0x0003c383 MyApp`-[NSObject(self=0x0b4e5210, _cmd=0x006ef5ad, selector=0x006f381a) performSelectorIfRespondsToIt:] + 243 at NSObject+PerformSelectorIfRespondsToIt.m:22
    frame #16: 0x00042506 MyApp`__44-[TourViewController closeTourWithAnimation]_block_invoke268(.block_descriptor=0x0b6985a0, finished='\x01') + 86 at TourViewController.m:419
    frame #17: 0x01af0df6 UIKit`-[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 223
    frame #18: 0x01ae3d66 UIKit`-[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 237
    frame #19: 0x01ae3f04 UIKit`-[UIViewAnimationState animationDidStop:finished:] + 68
    frame #20: 0x12b6ff28 UIKit`-[UIViewAnimationStateAccessibility(SafeCategory) animationDidStop:finished:] + 66
    frame #21: 0x018df7d8 QuartzCore`CA::Layer::run_animation_callbacks(void*) + 284
    frame #22: 0x03a90014 libdispatch.dylib`_dispatch_client_callout + 14
    frame #23: 0x03a807d5 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 296
    frame #24: 0x03f3eaf5 CoreFoundation`__CFRunLoopRun + 1925
    frame #25: 0x03f3df44 CoreFoundation`CFRunLoopRunSpecific + 276
    frame #26: 0x03f3de1b CoreFoundation`CFRunLoopRunInMode + 123
    frame #27: 0x044c67e3 GraphicsServices`GSEventRunModal + 88
    frame #28: 0x044c6668 GraphicsServices`GSEventRun + 104
    frame #29: 0x01aa5ffc UIKit`UIApplicationMain + 1211
    frame #30: 0x0000285d MyApp`main(argc=1, argv=0xbffff3f4) + 141 at main.m:16
    frame #31: 0x00002785 MyApp`start + 53
* thread #1: tid = 0x1a03, 0x01055c11 CoreGraphics`CG::Path::apply(void*, void (*)(void*, CGPathElementType, CGPoint const*)) const + 11, stop reason = EXC_BAD_ACCESS (code=2, address=0x0)
    frame #0: 0x01055c11 CoreGraphics`CG::Path::apply(void*, void (*)(void*, CGPathElementType, CGPoint const*)) const + 11
    frame #1: 0x00f5d090 CoreGraphics`CGPathApply + 64
    frame #2: 0x01838c68 QuartzCore`CA::Render::Path::new_path(CGPath const*, bool) + 158
    frame #3: 0x018c3493 QuartzCore`-[CAKeyframeAnimation _setCARenderAnimation:layer:] + 176
    frame #4: 0x018c3b2c QuartzCore`-[CAKeyframeAnimation _copyRenderAnimationForLayer:] + 68
    frame #5: 0x018c6f12 QuartzCore`-[CAAnimationGroup _copyRenderAnimationForLayer:] + 241
    frame #6: 0x018df547 QuartzCore`CA::Layer::commit_animations(CA::Transaction*, double (*)(CA::Layer*, double, void*), void (*)(CA::Layer*, CA::Render::Animation*, void*), void (*)(CA::Layer*, void**, void*), void*) + 641
    frame #7: 0x01855520 QuartzCore`CA::Context::commit_layer(CA::Layer*, unsigned int, unsigned int, void*) + 94
    frame #8: 0x018d87fa QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 330
    frame #9: 0x018d877e QuartzCore`CA::Layer::commit_if_needed(CA::Transaction*, void (*)(CA::Layer*, unsigned int, unsigned int, void*), void*) + 206
    frame #10: 0x01856667 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 1775
    frame #11: 0x01857227 QuartzCore`CA::Transaction::commit() + 395
    frame #12: 0x018f9b17 QuartzCore`+[CATransaction commit] + 52
    frame #13: 0x000399bf MyApp`-[HomeViewController catchTourScrollViewScreenshotViewAndAnimateItToButton](self=0x0b4e5210, _cmd=0x006f381a) + 1903 at HomeViewController.m:197
    frame #14: 0x0391f663 libobjc.A.dylib`-[NSObject performSelector:] + 62
    frame #15: 0x0003c383 MyApp`-[NSObject(self=0x0b4e5210, _cmd=0x006ef5ad, selector=0x006f381a) performSelectorIfRespondsToIt:] + 243 at NSObject+PerformSelectorIfRespondsToIt.m:22
    frame #16: 0x00042506 MyApp`__44-[TourViewController closeTourWithAnimation]_block_invoke268(.block_descriptor=0x0b6985a0, finished='\x01') + 86 at TourViewController.m:419
    frame #17: 0x01af0df6 UIKit`-[UIViewAnimationBlockDelegate _didEndBlockAnimation:finished:context:] + 223
    frame #18: 0x01ae3d66 UIKit`-[UIViewAnimationState sendDelegateAnimationDidStop:finished:] + 237
    frame #19: 0x01ae3f04 UIKit`-[UIViewAnimationState animationDidStop:finished:] + 68
    frame #20: 0x12b6ff28 UIKit`-[UIViewAnimationStateAccessibility(SafeCategory) animationDidStop:finished:] + 66
    frame #21: 0x018df7d8 QuartzCore`CA::Layer::run_animation_callbacks(void*) + 284
    frame #22: 0x03a90014 libdispatch.dylib`_dispatch_client_callout + 14
    frame #23: 0x03a807d5 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 296
    frame #24: 0x03f3eaf5 CoreFoundation`__CFRunLoopRun + 1925
    frame #25: 0x03f3df44 CoreFoundation`CFRunLoopRunSpecific + 276
    frame #26: 0x03f3de1b CoreFoundation`CFRunLoopRunInMode + 123
    frame #27: 0x044c67e3 GraphicsServices`GSEventRunModal + 88
    frame #28: 0x044c6668 GraphicsServices`GSEventRun + 104
    frame #29: 0x01aa5ffc UIKit`UIApplicationMain + 1211
    frame #30: 0x0000285d MyApp`main(argc=1, argv=0xbffff3f4) + 141 at main.m:16
    frame #31: 0x00002785 MyApp`start + 53

打开NSZombie不会提供更多有用的调试信息。

在升级到Xcode 4.6.3(4H1503)之前,相同的代码可以正常工作。需要注意的是,在模拟器中,动画有时会暂停,点击它会恢复它。这种奇怪的现象表明有些东西出了问题,但我还不知道是什么。

我错过了什么吗?
如果这是已知的错误,是否有任何解决方法?


一些研究笔记: 在SO上曾经提出过类似的问题,但被接受的答案只是通过将路径动画更改为线性平移来“修复”问题。

我的问题似乎也非常类似于这篇博客文章:http://www.blogosfera.co.uk/2013/08/exc_bad_access-while-using-coreanimation/


你的路径中只有一个部分,为什么需要加上空格呢? - David Rönnqvist
如果 xDistanceBetweenStartAndEndPoints 为零,则会出现除以零的情况。您的设置能保证这不会发生吗? - Dave DeLong
@DaveDeLong 是的,外部约束条件保证了xDistanceBetweenStartAndEndPoints不等于零。但我看不出除以零怎么会发生。难道运算符优先级不会使其计算为(1.0f / 3.0f) * xDistanceBetweenStartAndEndPoints吗? - Guillaume
@Guillaume 哦!你是对的。讨厌的运算顺序。 - Dave DeLong
1个回答

2
这不是一个非常具体的答案,而更像是一种帮助你找到更多线索的工具。希望有更多经验的人能够提供意见。
  • 开启malloc堆栈日志记录。
  • 重现崩溃。
  • 检查寄存器; 如果$r0或$r1看起来像是堆分配,则获取该地址的malloc历史记录。

如果两者中的最后一个事件是FREE,则数据缓冲区(或对象)会过早释放。(请参阅http://lldb.llvm.org/lldb-gdb.html以获取有关抓取malloc历史记录的信息)。

现在,即使它确认了这是一个过早释放,它可能并不能真正帮助你那么多。如果是这样,它将告诉你它是一种竞争条件。不幸的是,修复它并不是真正可行的,但可以采取解决方法。如果你能确定你控制哪个对象可能已经分配了过早的free()缓冲区,那么你应该能够持有一个强引用到该对象,直到动画完全完成。

还要报告一个错误,并附上你的崩溃二进制文件。


此外,打开malloc scribble。这将确保在释放内存时立即破坏它,这应该尽可能地使崩溃发生。


如果是一个过早释放的对象,那么Instruments的Zombies模板将有助于找到是哪个对象以及是什么导致了它的释放。 - Peter Hosey
@PeterHosey 只有当它真正是一个 Objective-C 对象时才行,但它似乎不是。这对 malloc 缓冲区不起作用(它仅适用于 ObjC 对象,因为 Zombie 实际上是一个代理,记录每个方法调用)。以上是试图从爆炸的缓冲区回溯到过早释放的对象。 - bbum
@bbum 很抱歉之前没有回复你,我有更紧急的事情要处理。我会尽快提交错误报告,一旦我有时间写它。我想让你知道,在使用iOS 7 SDK编译时,同样的错误也会发生。 - Guillaume

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接