即使你對自己的技術功底有再多的自信,都請養成使用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方法不會造成記憶體洩漏,下面我會給出良好的解決方法;
再看看接口
@property(nullable, strong) id <CAAnimationDelegate> delegate;
原來delegate是strong的,蘋果你咋不按套路出牌,蘋果為什麼要這樣設計呢?不管出于什麼原因,我們都要修複它
- (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委托方法即可。