系统相关
首页 > 系统相关> > iOS之深入解析内存管理NSTimer的强引用问题

iOS之深入解析内存管理NSTimer的强引用问题

作者:互联网

一、强引用问题分析

	self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(popHome) userInfo:nil repeats:YES];
	[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
	- (void)didMoveToParentViewController:(UIViewController *)parent {
	    if (parent == nil) {
	       [self.timer invalidate];
	        self.timer = nil;
	        NSLog(@"timer 被释放");
	    }
	}
	- (void)blockTimer {
	    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
	        NSLog(@"timer fire - %@", timer);
	    }];
	}
	The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to this object until it (the timer) is invalidated.
	timer强引用了target,直接对target所指向的内存地址强引用
	__weak typeof(self) weakSelf = self;
	self.timer = [NSTimer timerWithTimeInterval:1 target:weakSelf selector:@selector(popHome) userInfo:nil repeats:YES];
	[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

在这里插入图片描述

在这里插入图片描述

二、weakSelf 与 self

	NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)self));
	__weak typeof(self) weakSelf = self;
	NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)self));
	po weakSelf
	<ViewController: 0x7fea4f024200>
	po self
	<ViewController: 0x7fea4f024200>
	
	p &self
	(ViewController **) $4 = 0x00000001085a5fc8
	p &weakSelf
	(ViewController *const *) $5 = 0x00007ffeeb06b648

三、强引用的解决方案

① 当 controller 界面 pop 到上层界面的消除引用
	- (void)didMoveToParentViewController:(UIViewController *)parent {
	    if (parent == nil) {
	       [self.timer invalidate];
	        self.timer = nil;
	        NSLog(@"timer 被释放");
	    }
	}
② 中介者模式,不直接使用 self
	// 定义其他对象
	@property (nonatomic, strong) id            target;
	
	// 修改target
	self.target = [[NSObject alloc] init];
	class_addMethod([NSObject class], @selector(popHome), (IMP)popHomeObjc, "v@:");
	self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.target selector:@selector(popHome) userInfo:nil repeats:YES];
	
	// imp
	void popHomeObjc(id obj){
	    NSLog(@"%s -- %@", __func__, obj);
	}
	- (void)dealloc{
	    [self.timer invalidate];
	    self.timer = nil;
	    NSLog(@"%s", __func__);
	}
③ 自定义封装 timer
	// .h文件
	@interface YDWTimerWapper : NSObject
	
	- (instancetype)ydw_initWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
	- (void)ydw_invalidate;
	
	@end
	
	// .m文件
	#import "YDWTimerWapper.h"
	#import <objc/message.h>
	
	@interface YDWTimerWapper ()
	
	@property(nonatomic, weak) id target;
	@property(nonatomic, assign) SEL aSelector;
	@property(nonatomic, strong) NSTimer *timer;
	
	@end
	
	@implementation YDWTimerWapper
	
	- (instancetype)ydw_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(YDWTimerWapper *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)ydw_invalidate {
	    [self.timer invalidate];
	    self.timer = nil;
	}
	
	- (void)dealloc {
	    NSLog(@"%s",__func__);
	}
	
	@end
	// 定义
	self.timerWapper = [[YDWTimerWapper alloc] cjl_initWithTimeInterval:1 target:self selector:@selector(popHome) userInfo:nil repeats:YES];
	
	// 释放
	- (void)dealloc {
	     [self.timerWapper ydw_invalidate];
	}
④ 利用 NSProxy 虚基类的子类
	// NSProxy子类
	@interface YDWProxy : NSProxy
	+ (instancetype)proxyWithTransformObject:(id)object;
	@end
	
	@interface YDWProxy()
	@property (nonatomic, weak) id object;
	@end
	
	@implementation YDWProxy
	+ (instancetype)proxyWithTransformObject:(id)object{
	    YDWProxy *proxy = [YDWProxy alloc];
	    proxy.object = object;
	    return proxy;
	}
	- (id)forwardingTargetForSelector:(SEL)aSelector {
	    return self.object;
	}
	// 解决timer强持有问题
	self.proxy = [YDWProxy proxyWithTransformObject:self];
	self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self.proxy selector:@selector(popHome) userInfo:nil repeats:YES];
	
	// 在dealloc中将timer正常释放
	- (void)dealloc {
	    [self.timer invalidate];
	    self.timer = nil;
	}

标签:vc,target,self,iOS,timer,selector,weakSelf,内存,NSTimer
来源: https://blog.csdn.net/Forever_wj/article/details/118059625