即使你对自己的技术功底有再多的自信,都请养成使用Instruments工具排查内存泄漏的良好习惯,
即使Instruments再牛逼,你也还要养成看接口文档的良好习惯,因为你防谁也防不了苹果。。。
下面就是使用Instruments排查不出来的内存泄漏
直接上代码
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"colors"]; [animation setToValue:@[(__bridge id)DX_RGB(170, 127, 245).CGColor, (__bridge id)DX_RGB(137, 215, 244).CGColor,(__bridge id)DX_RGB(245, 214, 234).CGColor]]; [animation setDuration:3.0]; [animation setRemovedOnCompletion:NO]; [animation setFillMode:kCAFillModeForwards]; [animation setDelegate:self]; [gradientLayer addAnimation:animation forKey:@"animateGradient"];
gradientLayer是当前控制器的一个实例变量,最后退出当前视图控制器后,它没有调用dealloc,发生了内存泄漏,因为当前视图控制器和animation构成了强引用环。
将setRemovedOnCompletion改为YES或者屏蔽setDelegate方法不会造成内存泄漏,下面我会给出良好的解决方法;
再看看接口
/* The delegate of the animation. This object is retained for the
* lifetime of the animation object. Defaults to nil. See below for the
* supported delegate methods. */
@property(nullable, strong) id <CAAnimationDelegate> delegate;
原来delegate是strong的,苹果你咋不按套路出牌,苹果为什么要这样设计呢?不管出于什么原因,我们都要修复它
/* Called when the animation either completes its active duration or
* is removed from the object it is attached to (i.e. the layer). 'flag'
* is true if the animation reached the end of its active duration
* without being removed. */
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
尝试在这个回调方法中调用
[animation setDelegate:nil];
引发崩溃,信息如下:
*** Terminating app due to uncaught exception 'CAAnimationImmutable', reason: 'attempting to modify read-only animation <CABasicAnimation: 0x1740315c0>'
完美解决方法:
实现分类:
#import <objc/runtime.h> static void* MyBasicAnimationKey = "MyBasicAnimationKey"; @interface CABasicAnimation(BUG)<CAAnimationDelegate> - (void)setDebugDelegate:(id)delegate; @end @implementation CABasicAnimation(BUG) - (void)setDebugDelegate:(id)delegate { self.delegate = self;//将委托指向自己,并实现委托方法 objc_setAssociatedObject(self, MyBasicAnimationKey, delegate, OBJC_ASSOCIATION_ASSIGN);//这里通过对象关联来实现,注意这里必须是OBJC_ASSOCIATION_ASSIGN,而不能用OBJC_ASSOCIATION_RETAIN,否则仍然是强引用环。 } #pragma mark - CAAnimationDelegate - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { id obj = objc_getAssociatedObject ( self, MyBasicAnimationKey ); [obj animationDidStop:anim finished:flag];//这里将实现转给关联对象 } @end
然后在DebugDelegate中实现- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag委托方法即可。