本次代碼實(shí)踐源自一次面試.
Q:平時(shí)使用定時(shí)器NSTimer時(shí)如何釋放應(yīng)用它的target?
A:在viewDidDisappear中將調(diào)用invalid方法,在將timer置為nil.
但面試官想要的好像不是這個(gè)回答.
下面是我自己的答案的代碼實(shí)踐
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer scheduledTimerWithTimeInterval:2
target:self
selector:@selector(timeAction:)
userInfo:nil
repeats:YES];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.timer invalidate];
self.timer = nil;
}
上面代碼self應(yīng)用了timer,而timer頁(yè)應(yīng)用了self.這就形成了一個(gè)互相應(yīng)用,肯定會(huì)有內(nèi)存泄漏的.
我在dealloc方法打了斷點(diǎn),當(dāng)頁(yè)面消失后,的確會(huì)走到dealloc方法,說(shuō)明這樣做是可以釋放當(dāng)前self的.猜測(cè)應(yīng)該是viewDidDisappear中的操作斷開(kāi)了timer對(duì)self的應(yīng)用,從而內(nèi)存得以釋放.
下面是面試官給的思路
上面寫(xiě)的代碼導(dǎo)致內(nèi)存釋放不了的就是self和timer之間互相應(yīng)用了,想要self的釋放不受timer影響就必須斷開(kāi)二者的互相應(yīng)用.
關(guān)鍵點(diǎn)在于開(kāi)啟定時(shí)器時(shí)的target設(shè)置
我們定義一個(gè)中間件作為定時(shí)器的target,利用delegate回調(diào)定時(shí)器事件,由于delegate的弱應(yīng)用,就實(shí)現(xiàn)了timer對(duì)self的弱應(yīng)用.
//DetailViewController.m
#import "DetailViewController.h"
#import "Middleware.h"
@interface DetailViewController ()<MiddlewareDelegate>
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) Middleware *middleware;
@end
@implementation DetailViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.middleware = [[Middleware alloc] initWithDelegate:self];
self.timer = [NSTimer scheduledTimerWithTimeInterval:2
target:self.middleware
selector:@selector(timeAction:)
userInfo:nil
repeats:YES];
}
- (void)dealloc {
}
- (void)delegateTimerAction {
NSLog(@"%f",CACurrentMediaTime());
}
@end
//Middleware.m
#import "Middleware.h"
@interface Middleware()
@property (nonatomic, weak) id<MiddlewareDelegate> delegate;
@end
@implementation Middleware
- (instancetype)initWithDelegate:(id<MiddlewareDelegate>)delegate {
self = [super init];
if (self) {
self.delegate = delegate;
}
return self;
}
- (void)timeAction:(NSTimer *)timer {
[self.delegate delegateTimerAction];
}
@end
當(dāng)我們推出當(dāng)前頁(yè)面時(shí),dealloc方法也會(huì)被調(diào)用,說(shuō)明當(dāng)前類(lèi)被釋放了.
從iOS10開(kāi)始,蘋(píng)果新增了3個(gè)創(chuàng)建定時(shí)器的方法用于避免循環(huán)引用
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval
repeats:(BOOL)repeats
block:(void (^)(NSTimer *timer))block;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
repeats:(BOOL)repeats
block:(void (^)(NSTimer *timer))block;
- (instancetype)initWithFireDate:(NSDate *)date
interval:(NSTimeInterval)interval
repeats:(BOOL)repeats
block:(void (^)(NSTimer *timer))block;
這三個(gè)方法都將定時(shí)器要執(zhí)行的操作放在了block中,并且將當(dāng)前timer作為block的參數(shù)傳進(jìn)去了.
思考
對(duì)于第一種方法有個(gè)問(wèn)題,如果不是推出當(dāng)前頁(yè)面,而push到另一個(gè)頁(yè)面,每次回來(lái)都要銷(xiāo)毀創(chuàng)建創(chuàng)建銷(xiāo)毀.
對(duì)于平時(shí)使用的api,只是簡(jiǎn)單的會(huì)用,沒(méi)有深入去理解內(nèi)在的機(jī)制.以后要多去了解內(nèi)在機(jī)制、原理.