内存泄漏的表现
如图, 在vc中,将计时器timer 属性特性设置为weak,创建一个自动添加到runloop中的计时器
///计时器方法
/// vc的dealloc
然而当VC退出的时候,并没有执行dealloc
由此看到,虽然timer和VC之间没有循环引用(timer 是weak的),但是VC并没有释放
NSTimer 内存泄漏的原因
由于timer是添加到runloop 中的,由此可推测,将timer 添加到runloop的过程中,
runloop 强持有了timer, 又因为timer强持有target,由于runloop是
一直存在的(只要该线程一直存在),就造成了target被一直持有而不释放,从而造成内存泄漏
如图
解决方案
添加一个中间实例对象a,该对象a对真实的target b弱引用,将timer 直接的target设置为该中间实例,从而解除timer对真实的target b 强持有,避免内存泄漏
逻辑如图
#实现代码
.h
//
// NSTimer+lbTimer.h
// LIUBO
//
// Created by liubo on 2018/8/2.
// Copyright © 2018年 iOS. All rights reserved.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface LBTimerTarget : NSObject
@end
@interface NSTimer (LBTimer)
/**
普通定时器,自动加入Default类型的runloop中
*/
+ (NSTimer *)lb_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
target:(id)target
repeat:(BOOL)repeat
block:(void(^)(NSTimer *timer))block;
/**
普通定时器,需要手动添加至runloop中
*/
+ (NSTimer *)lb_timerWithTimeInterval:(NSTimeInterval)interval
target:(id)target
repeat:(BOOL)repeat
block:(void(^)(NSTimer *timer))block;
/**
普通定时器,需要手动添加至runloop中,可以进行暂停,重启操作
*/
+ (NSTimer *)lb_timerWithTimeInterval:(NSTimeInterval)interval
target:(id)target
selector:(SEL)selector
userInfo:(nullable id)userInfo
repeats:(BOOL)repeat;
@end
NS_ASSUME_NONNULL_END
.m
//
// NSTimer+lbTimer.m
// LIUBO
//
// Created by liubo on 2018/8/2.
// Copyright © 2018年 iOS. All rights reserved.
//
#import "NSTimer+LBTimer.h"
@interface LBTimerTarget ()
@property (nonatomic,weak) id sourceTarget;
@property (nonatomic,copy) void(^actionBlock)(NSTimer *timer);
@property (nonatomic, assign) SEL actionSelector;
@end
@implementation LBTimerTarget
- (instancetype)initWithSelector:(SEL)aSelector sourceTarget:(id)sourceTarget{
self = [super init];
if (self) {
self.actionBlock = nil;
self.sourceTarget = sourceTarget;
self.actionSelector = aSelector;
}
return self;
}
- (void)dealloc
{
}
- (instancetype)initWithBlock:(void(^)(NSTimer *timer))block sourceTarget:(id)sourceTarget{
self = [super init];
if (self) {
self.actionBlock = block;
self.sourceTarget =sourceTarget;
}
return self;
}
- (void)timerAction:(NSTimer *)timer{
if (self.sourceTarget == nil) {
[timer invalidate];
timer = nil;
}else{
if (self.actionBlock) {
self.actionBlock(timer);
}else{
IMP imp = [self.sourceTarget methodForSelector:self.actionSelector];
void (*func)(id, SEL,NSTimer *) = (void *)imp;
func(self.sourceTarget, self.actionSelector,timer);
}
}
}
@end
#pragma mark - NSTimer Category
@implementation NSTimer (LBTimer)
+ (NSTimer *)lb_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
target:(id)target
repeat:(BOOL)repeat
block:(void(^)(NSTimer *timer))block
{
LBTimerTarget *timerTarget = [[LBTimerTarget alloc] initWithBlock:block sourceTarget:target];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:interval target:timerTarget selector:@selector(timerAction:) userInfo:nil repeats:YES];
return timer;
}
+ (NSTimer *)lb_timerWithTimeInterval:(NSTimeInterval)interval
target:(id)target
repeat:(BOOL)repeat
block:(void(^)(NSTimer *timer))block
{
LBTimerTarget *timerTarget = [[LBTimerTarget alloc] initWithBlock:block sourceTarget:target];
NSTimer *timer = [NSTimer timerWithTimeInterval:interval target:timerTarget selector:@selector(timerAction:) userInfo:nil repeats:YES];
return timer;
}
+ (NSTimer *)lb_timerWithTimeInterval:(NSTimeInterval)interval
target:(id)target
selector:(SEL)selector
userInfo:(nullable id)userInfo
repeats:(BOOL)repeat
{
LBTimerTarget *timerTarget = [[LBTimerTarget alloc] initWithSelector:selector sourceTarget:target];
NSTimer *timer = [NSTimer timerWithTimeInterval:interval target:timerTarget selector:@selector(timerAction:) userInfo:userInfo repeats:repeat];
return timer;
}
@end
测试结果,正常释放