强引用(强持有)
解决NSTimer强引用:
- 重写didMoveToParentViewController方法
- (void)didMoveToParentViewController:(UIViewController *)parent{ // 无论push 进来 还是 pop 出去 正常跑 // 就算继续push 到下一层 pop 回去还是继续 if (parent == nil) { [self.timer invalidate]; self.timer = nil; NSLog(@"timer 走了"); } }
- 定义timer时,采用闭包的形式,因此不需要指定target
- (void)blockTimer{ self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) { NSLog(@"timer fire - %@",timer); }]; }
解决 强引用(强持有)
依赖中介者模式,打破强持有,其中推荐思路四
1. pop时在其他方法中销毁timer
- (void)didMoveToParentViewController:(UIViewController *)parent{ // 无论push 进来 还是 pop 出去 正常跑 // 就算继续push 到下一层 pop 回去还是继续 if (parent == nil) { [self.timer invalidate]; self.timer = nil; NSLog(@"timer 走了"); } }
2.中介者模式,即不使用self,依赖于其他对象
//**********1、定义其他对象********** @property (nonatomic, strong) id target; //**********1、修改target********** self.target = [[NSObject alloc] init]; class_addMethod([NSObject class], @selector(fireHome), (IMP)fireHomeObjc, "v@:"); self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.target selector:@selector(fireHome) userInfo:nil repeats:YES]; //**********3、imp********** void fireHomeObjc(id obj){ NSLog(@"%s -- %@",__func__,obj); } - (void)dealloc{ [self.timer invalidate]; self.timer = nil; NSLog(@"%s",__func__); }
3.自定义封装timer
//*********** .h文件 *********** @interface CJLTimerWapper : NSObject - (instancetype)cjl_initWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo; - (void)cjl_invalidate; @end //*********** .m文件 *********** #import "CJLTimerWapper.h" #import <objc/message.h> @interface CJLTimerWapper () @property(nonatomic, weak) id target; @property(nonatomic, assign) SEL aSelector; @property(nonatomic, strong) NSTimer *timer; @end @implementation CJLTimerWapper - (instancetype)cjl_initWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo{ if (self == [super init]) { //传入vc self.target = aTarget; //传入的定时器方法 self.aSelector = aSelector; if ([self.target respondsToSelector:self.aSelector]) { Method method = class_getInstanceMethod([self.target class], aSelector); const char *type = method_getTypeEncoding(method); //给timerWapper添加方法 class_addMethod([self class], aSelector, (IMP)fireHomeWapper, type); //启动一个timer,target是self,即监听自己 self.timer = [NSTimer scheduledTimerWithTimeInterval:ti target:self selector:aSelector userInfo:userInfo repeats:yesOrNo]; } } return self; } //一直跑runloop void fireHomeWapper(CJLTimerWapper *wapper){ //判断target是否存在 if (wapper.target) { //如果存在则需要让vc知道,即向传入的target发送selector消息,并将此时的timer参数也一并传入,所以vc就可以得知`fireHome`方法,就这事这种方式定时器方法能够执行的原因 //objc_msgSend发送消息,执行定时器方法 void (*lg_msgSend)(void *,SEL, id) = (void *)objc_msgSend; lg_msgSend((__bridge void *)(wapper.target), wapper.aSelector,wapper.timer); }else{ //如果target不存在,已经释放了,则释放当前的timerWrapper [wapper.timer invalidate]; wapper.timer = nil; } } //在vc的dealloc方法中调用,通过vc释放,从而让timer释放 - (void)cjl_invalidate{ [self.timer invalidate]; self.timer = nil; } - (void)dealloc { NSLog(@"%s",__func__); } @end
4. 利用NSProxy虚基类的子类
//************NSProxy子类************ @interface CJLProxy : NSProxy + (instancetype)proxyWithTransformObject:(id)object; @end @interface CJLProxy() @property (nonatomic, weak) id object; @end @implementation CJLProxy + (instancetype)proxyWithTransformObject:(id)object{ CJLProxy *proxy = [CJLProxy alloc]; proxy.object = object; return proxy; } -(id)forwardingTargetForSelector:(SEL)aSelector { return self.object; }
//************解决timer强持有问题************ self.proxy = [CJLProxy proxyWithTransformObject:self]; self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.proxy selector:@selector(fireHome) userInfo:nil repeats:YES]; //在dealloc中将timer正常释放 - (void)dealloc{ [self.timer invalidate]; self.timer = nil; }