CoreAnimation与内存泄漏。

最近调查的一个bug和内存泄露都和CoreAnimation有关,因此谈一下使用CoreAnimation需要注意的几个问题

  1. CAAnimation的delegate属性是retain的,这个设计确实比较坑人,完全违背了“一致性”原则,产品里面的有个内存泄露就和这个有关。
  2. CALayer的addAnimation:forKey方法会对第一个参数,也即animation对象进行copy;这种设计的一个结果是,如果你同时添加了多个animation,在CAAnimation的delegate方法animationDidStop:finished里面无法通过比较引用来区分animation;要想达到区分的目的,只能通过[CAAnimation setValue:forKey]设定一个属性,比如ID,然后在delegate方法里面去比较这个属性值。
  3. 尽量不要将CAAnimation的removedOnCompletion设置为NO,否则的话,很容易发生内存泄露;参考第1点,常见的场景是有一个view,里面创建了一个CAAnimation添加给了自身,CAAnimation的delegate设成了view自身,至此一个循环引用形成
    CAAnimation *animation = ...
    animation.delegate = self;
    animation.removeOnComplete=NO;
    [self.layer addAnimation:animation];
  4. 很多人之所以将removedOnCompletion设置成为YES,是为了在CAAnimation结束之后,CALayer不要回退到动画前的状态,这个正确的解决方案是这样的,在动画开始之前将layer的相关属性设置为目标值
    CABasicAnimation *animation = [CABasicAnimation  animationWithProperty:@property];
    animation.fromValue = @fValue;
    animation.toValue = @tValue;
    [layer addAnimation:animation forKey:@key];
    layer.property = tValue //设置layout的属性值为目标值
  5. 要理解上面这个方案之所以是正确的,必须要稍微了解一下CALayer动画的原理,每个CALayer有一个对应的present layer用来做动画,此时自身可以叫做model layer,顾名思义,model layer是用来保存相对稳定的属性的,而present layer使用来临时渲染动画效果的。在动画运行的过程中,屏幕上显示的是present layer而不是model layer,animation对象对属性值进行插值处理的目标也是present layer,当动画结束后,model layer又显示出来了。理解了这一点,很多CAAnimation的现象就不难理解了,removedOnCompletion属性设置为YES,看起来确实可以使layer保持动画的结束状态,因为显示的一直就是present layer。而动画开始之前,将layer的属性设置为目标属性,也不会和动画相互产生作用,因为动画根本不会去修改layer的属性。