在iOS中,NSTimer
是咱們日常都會用到的定時器。因此咱們有必要了解其相關的使用方法和須要注意的點。在咱們建立NSTimer
定時器是timer
是強引用target
的這樣就造成了循環引用的問題,從而致使內存泄漏。要避免這樣的問題咱們可使用一下方法:ios
- (void)didMoveToParentViewController:(UIViewController *)parent
中銷燬timer
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"======== ViewController1 將要加載視圖: viewDidLoad =======\n");
//iOS10
// self.timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
//
// }];
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(changeTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
- (void)changeTime {
}
- (void)didMoveToParentViewController:(UIViewController *)parent {
if (!parent) {
[self.timer invalidate];
self.timer = nil;
}
}
- (void)dealloc {
NSLog(@"======== ViewController1 釋放: dealloc =======\n");
}
複製代碼
block
方式添加target-action
給NSTimer
分類添加方法以下:+ (NSTimer *)S_timerWithTimeInterval:(NSTimeInterval)ti block:(void(^)(void))block userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo {
return [self timerWithTimeInterval:ti target:self selector:@selector(blockSelector:) userInfo:[block copy] repeats:yesOrNo];
}
+ (void)blockSelector:(NSTimer *)timer {
void(^block)(void) = timer.userInfo;
if (block) {
block();
}
}
複製代碼
調用bash
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer S_timerWithTimeInterval:1 block:^{
[weakSelf changeTime];
} userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
- (void)changeTime {
}
複製代碼
經過block
的方式將action
傳遞出來,實際上timer的target是NSTimer類。即這樣就不會產生循環引用。使用中因爲使用了block因此要注意其引發的循環引用。 3. 定義中介繼承NSProxy進行消息轉發,消除強引用 關於NSProxy類的有關知識能夠看連接的資料,這裏就再也不復述了。async
@property (nonatomic, weak) id target;
+ (instancetype)proxyWithWeakTarget:(id)target;
- (id)initWithTarget:(id)target;
@end
@implementation SProxy
+ (instancetype)proxyWithWeakTarget:(id)target {
return [[SProxy alloc] initWithTarget:target];
}
- (id)initWithTarget:(id)target {
_target = target;
return self;
}
///將方法的簽名(SEL)轉發給真正實現了該消息的對象
- (void)forwardInvocation:(NSInvocation *)invocation {
if ([_target respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget:_target];
}
}
///爲另外一個類實現的消息建立一個有效的方法簽名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [_target methodSignatureForSelector:sel];
}
@end
複製代碼
NSObject
進行消息轉發消除強引用@interface TimerTraget : NSObject
@property (nonatomic, weak) id target;
+ (instancetype)timerTragetWithTarget:(id)target;
@end
+ (instancetype)timerTragetWithTarget:(id)target {
TimerTraget *timer = [[TimerTraget alloc] init];
timer.target = target;
return timer;
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.target respondsToSelector:aSelector]) {
return self.target;
}
return nil;
}
///使用方法以下
self.timer = [NSTimer timerWithTimeInterval:1 target:[TimerTraget timerTragetWithTarget:self] selector:@selector(changeTime) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
- (void)changeTime {
}
複製代碼
在咱們的平時開發中常常會用到定時器 ,相對於NSTimer實現的定時器,GCD定時器記錄的時間相對要精確一些。並且不用考慮內存釋放的問題。ide
如下是GCD實現基本的獲取驗證碼的倒計時功能 直接上代碼了oop
- (void)viewDidLoad {
[super viewDidLoad];
self.but = [[UIButton alloc] initWithFrame:CGRectMake(self.view.center.x, self.view.center.y, 100, 50)];
// self.but.backgroundColor = [UIColor redColor];
[self.but setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal];
[self.but setTitle:@"獲取驗證碼" forState:UIControlStateNormal];
self.but.titleLabel.font = [UIFont systemFontOfSize:15];
[self.but addTarget:self action:@selector(tapClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.but];
}
- (void)tapClick:(UIButton*)but{
[self playGCDTimer];
}
- (void)playGCDTimer{
__block NSInteger time = 59;
//全局隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//建立timer 類型的定時器 (DISPATCH_SOURCE_TYPE_TIMER)
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//設置定時器的各類屬性(什麼時候開始,間隔多久執行)
// GCD 的時間參數通常爲納秒 (1 秒 = 10 的 9 次方 納秒)
// 指定定時器開始的時間和間隔的時間
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//任務回調
dispatch_source_set_event_handler(timer, ^{
if (time <= 0) {
dispatch_source_cancel(timer);//關閉定時器
dispatch_async(dispatch_get_main_queue(), ^{
[self.but setTitle:@"從新發送" forState:UIControlStateNormal];
self.but.userInteractionEnabled = YES;
});
}else{
int seconds = time % 60;
dispatch_sync(dispatch_get_main_queue(), ^{
[self.but setTitle:[NSString stringWithFormat:@"從新發送(%.2d)",seconds] forState:UIControlStateNormal];
self.but.userInteractionEnabled = NO;//驗證碼獲取時禁止用戶點擊
});
}
time--;
});
// 開始定時器任務(定時器默認開始是暫停的,須要復位開啓)
dispatch_resume(timer);
}
複製代碼
好了GCD建立定時器的基本方法就介紹完了ui