強引用(強持有)
解決NSTimer強引用:
- 重寫didMoveToParentViewController方法
- (void)didMoveToParentViewController:(UIViewController *)parent{
// 無論push 進來 還是 pop 出去 正常跑
// 就算繼續(xù)push 到下一層 pop 回去還是繼續(xù)
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 出去 正常跑
// 就算繼續(xù)push 到下一層 pop 回去還是繼續(xù)
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,即監(jiān)聽自己
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發(fā)送selector消息,并將此時的timer參數(shù)也一并傳入,所以vc就可以得知`fireHome`方法,就這事這種方式定時器方法能夠執(zhí)行的原因
//objc_msgSend發(fā)送消息,執(zhí)行定時器方法
void (*lg_msgSend)(void *,SEL, id) = (void *)objc_msgSend;
lg_msgSend((__bridge void *)(wapper.target), wapper.aSelector,wapper.timer);
}else{
//如果target不存在,已經(jīng)釋放了,則釋放當(dāng)前的timerWrapper
[wapper.timer invalidate];
wapper.timer = nil;
}
}
//在vc的dealloc方法中調(diào)用,通過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;
}