[iOS]定时器NSTimer、CADisplayLink的内存管理
程序员文章站
2022-03-12 20:25:38
NSTimer、CADisplayLink会对target产生强引用,如果target同时对他们产生强引用,则会发生循环引用。 以NSTimer为例,解决循环引用的问题。 方法1:使用block - (void)viewDidLoad { [super viewDidLoad]; // Do any ......
nstimer、cadisplaylink会对target产生强引用,如果target同时对他们产生强引用,则会发生循环引用。
以nstimer为例,解决循环引用的问题。
方法1:使用block
- (void)viewdidload { [super viewdidload]; // do any additional setup after loading the view. __weak typeof(self) weakself = self; self.timer = [nstimer scheduledtimerwithtimeinterval:1.0 repeats:yes block:^(nstimer * _nonnull timer) { [weakself func]; }]; } - (void)func { nslog(@"%s",__func__); } - (void)dealloc { nslog(@"%s",__func__); [self.timer invalidate]; }
方法2:使用nsobject作为中间对象
proxy1.h @interface proxy1 : nsobject + (instancetype)initwithtarget:(id)target; @end
proxy1.m @interface proxy1 () @property (nonatomic,weak) id target; @end @implementation proxy1 + (instancetype)initwithtarget:(id)target { proxy1 *proxy = [[proxy1 alloc] init]; proxy.target = target; return proxy; } - (id)forwardingtargetforselector:(sel)aselector { return self.target; } @end
- (void)viewdidload { [super viewdidload]; // do any additional setup after loading the view. self.timer = [nstimer scheduledtimerwithtimeinterval:1.0 target:[proxy1 initwithtarget:self] selector:@selector(func) userinfo:nil repeats:yes]; } - (void)func { nslog(@"%s",__func__); } - (void)dealloc { nslog(@"%s",__func__); [self.timer invalidate]; }
方法3:使用nsproxy作为中间对象
proxy2.h @interface proxy2 : nsproxy + (instancetype)initwithtarget:(id)target; @end
proxy2.m @interface proxy2 () @property (nonatomic,weak) id target; @end @implementation proxy2 + (instancetype)initwithtarget:(id)target { proxy2 *proxy = [proxy2 alloc]; proxy.target = target; return proxy; } - (nsmethodsignature *)methodsignatureforselector:(sel)sel { return [self.target methodsignatureforselector:sel]; } - (void)forwardinvocation:(nsinvocation *)invocation { [invocation invokewithtarget:self.target]; } @end
- (void)viewdidload { [super viewdidload]; // do any additional setup after loading the view. self.timer = [nstimer scheduledtimerwithtimeinterval:1.0 target:[proxy2 initwithtarget:self] selector:@selector(func) userinfo:nil repeats:yes]; } - (void)func { nslog(@"%s",__func__); } - (void)dealloc { nslog(@"%s",__func__); [self.timer invalidate]; }
方法3的优点:
执行效率高,无需执行父类的方法搜索过程,直接进行消息转发。
关于nsproxy补充:
通过调用iskindofclass
proxy1 *proxy1 = [proxy1 initwithtarget:self]; proxy2 *proxy2 = [proxy2 initwithtarget:self]; nslog(@"%d",[proxy1 iskindofclass:[viewcontroller class]]); // 0 nslog(@"%d",[proxy2 iskindofclass:[viewcontroller class]]); // 1
proxy1为proxy1类型,proxy1继承自nsobject,可以正常处理iskindofclass方法,所以判断结果为0.
proxy2为proxy2类型,proxy2继承自nsproxy,大部分方法会直接进入消息转发阶段,会改为使用target进行调用,所以判断结果为1.
通过观察nsproxy的源码发现,该方法直接进行了消息转发。
/** * calls the -forwardinvocation: method to determine if the 'real' object * referred to by the proxy is an instance of the specified class. * returns the result.<br /> * nb. the default operation of -forwardinvocation: is to raise an exception. */ - (bool) iskindofclass: (class)aclass { nsmethodsignature *sig; nsinvocation *inv; bool ret; sig = [self methodsignatureforselector: _cmd]; inv = [nsinvocation invocationwithmethodsignature: sig]; [inv setselector: _cmd]; [inv setargument: &aclass atindex: 2]; [self forwardinvocation: inv]; [inv getreturnvalue: &ret]; return ret; }