iOS定時器相關實踐

NSTimer定時器

在iOS中,NSTimer是咱們日常都會用到的定時器。因此咱們有必要了解其相關的使用方法和須要注意的點。在咱們建立NSTimer定時器是timer是強引用target的這樣就造成了循環引用的問題,從而致使內存泄漏。要避免這樣的問題咱們可使用一下方法:ios

  1. 在合適的地方銷燬NSTimer 在咱們離開當前視圖時,咱們能夠在- (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");
}

複製代碼
  1. 使用block方式添加target-actionNSTimer分類添加方法以下:
+ (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
複製代碼
  1. 定義中介值繼承自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 {
    
}
複製代碼

GCD定時器

在咱們的平時開發中常常會用到定時器 ,相對於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

相關文章
相關標籤/搜索