简化NSTimer的使用

NSTimer的使用很方便也很简单,但使用时需要把创建的timer定义成一个成语变量或属性持有,不然调用的block会一直执行,及时当前对象已经销毁了,block还会执行

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(10, 80, 40, 20)];
    lbl.textColor = [UIColor blackColor];
    lbl.font = [UIFont systemFontOfSize:14];
    lbl.backgroundColor = [UIColor redColor];
    lbl.text = @"0---";
    lbl.textAlignment = NSTextAlignmentCenter;
    [self.view addSubview:lbl];
    
    __weak typeof(self) weakSelf = self;
    [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"weakself %@ %@",weakSelf,lbl.text);
    }];
//    [ZQQTimer timerWithHolder:self delay:0 TimeInterval:0 block:^(ZQQTimer *timer, NSInteger repeatCount) {
//        NSLog(@"aaa %ld %@",repeatCount,timer);
//        if (repeatCount == 5) {
//            [timer invalidate];
//        }
//    }];
//
//    
//    [self startTimerDelay:0 timeInterval:1 block:^(ZQQTimer *timer, NSInteger repeatCount) {
//        NSLog(@"%ld timer:%@",repeatCount,timer);
//        lbl.text = [NSString stringWithFormat:@"%ld",repeatCount];
//    }];
//    
//    [self startTimerWithBlock:^(ZQQTimer * timer, NSInteger repeatCount) {
//        
//    }];
}
- (void)dealloc
{
    NSLog(@"delloc %s",__FUNCTION__);
}

<pre>
2017-05-22 15:37:42.762 YZDisplayViewControllerDemo[31145:3580555] weakself <TestZQQTimerViewController: 0x7fe4d8549b70> 0---
2017-05-22 15:37:43.763 YZDisplayViewControllerDemo[31145:3580555] weakself <TestZQQTimerViewController: 0x7fe4d8549b70> 0---
2017-05-22 15:37:44.313 YZDisplayViewControllerDemo[31145:3580555] delloc -[TestZQQTimerViewController dealloc]
2017-05-22 15:37:44.763 YZDisplayViewControllerDemo[31145:3580555] weakself (null) 0---
2017-05-22 15:37:45.763 YZDisplayViewControllerDemo[31145:3580555] weakself (null) 0---
2017-05-22 15:37:46.763 YZDisplayViewControllerDemo[31145:3580555] weakself (null) 0---
2017-05-22 15:37:47.763 YZDisplayViewControllerDemo[31145:3580555] weakself (null) 0---
</pre>

现在特意封装了一下NSTimer,让在开发或调试时随意使用NSTimer而不用专门定义一个变量来持有timer,同时随着使用timer的对象销毁后,timer也自动销毁,以提高开发效率
基本原理如下:
1、虽然表面使用者没有专门定义一个变量来持有timer,但实际上通过runtime的方式来持有该timer
2、由于使用runtime的objc_getAssociatedObject/objc_setAssociatedObject的方式持有一个间接变量,所以间接变量会随着调用timer的这个宿主销毁而销毁,在销毁时自动同事销毁timer
3、添加一个使用timer时常用的计数器传递出来,这样方便timer的使用
4、自定义的ZQQTimer实现了invalidate方法,可以根据业务逻辑需要手动代码关闭timer
具体代码如下
ZQQTimer.h

//
//  ZQQTimer.h
//  zqqkit
//
//  Created by liuhuan on 2017/5/22.
//  Copyright © 2017年 zqq.love All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class ZQQTimer;
typedef void(^ZQQTimerBlock)(ZQQTimer * _Nonnull timer,NSInteger repeatCount);
@interface ZQQTimer : NSObject

+ (ZQQTimer *_Nonnull)timerWithHolder:(nonnull id)holder delay:(NSTimeInterval)delay TimeInterval:(NSTimeInterval)timeInterval block:(ZQQTimerBlock _Nullable )block;

- (void)invalidate;
@property (readonly, getter=isValid) BOOL valid;
@property (nonatomic,assign) NSInteger repeatCount;

@end

ZQQTimer.m

//
//  ZQQTimer.m
//  ZQQKit
//
//  Created by liuhuan on 2017/5/22.
//  Copyright © 2017年 zqq.love. All rights reserved.
//

#import "ZQQTimer.h"
#import "NSObject+ZQQBindData.h"

#define kSaveZQQTimerInOneHolderKey @"kSaveZQQTimerInOneHolderKey"

@interface ZQQTimer()
@property (nonatomic,copy) ZQQTimerBlock block;
@property (nonatomic,strong) NSTimer * timer;
@property (nonatomic,weak) id holder;
@property (nonatomic,assign) NSTimeInterval timeInterval;
@property (nonatomic,assign) NSTimeInterval delay;
@end
@implementation ZQQTimer
+ (ZQQTimer *)timerWithHolder:(id)holder delay:(NSTimeInterval)delay TimeInterval:(NSTimeInterval)timeInterval block:(ZQQTimerBlock)block
{
   
    ZQQTimer *zqqTimer = [ZQQTimer new];
    NSMutableDictionary *dictM = [holder valueForKey:kSaveZQQTimerInOneHolderKey defaultValue:[NSMutableDictionary dictionary]];
    
    
    dictM[zqqTimer.description] = zqqTimer;
    zqqTimer.block = [block copy];
    zqqTimer.holder = holder;
    zqqTimer.timeInterval = timeInterval;
    zqqTimer.delay = delay;
    
    [zqqTimer showTimer];
    return zqqTimer;
}
- (void)showTimer
{
    
    if (self.timeInterval == 0) {
        self.timeInterval = 1;
    }
    __weak typeof(self) weakSelf = self;
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        weakSelf.timer = [NSTimer scheduledTimerWithTimeInterval:self.timeInterval repeats:YES block:^(NSTimer * _Nonnull timer) {
            __strong typeof(weakSelf) strongSelf = weakSelf;
            strongSelf.repeatCount += 1;
            
            if (strongSelf.block) {
                strongSelf.block(strongSelf,strongSelf.repeatCount);
            }
            // -----防止数字越界 快捷键为gg
            if (strongSelf.repeatCount == NSIntegerMax) {
                strongSelf.repeatCount = 0;
            }
        }];
    });
   
}
- (void)invalidate
{
    [self.timer invalidate];
    self.timer = nil;
    NSMutableDictionary *dictM = self.holder[kSaveZQQTimerInOneHolderKey];
    [dictM removeObjectForKey:self.description];
    
}
- (BOOL)isValid
{
    return [self.timer isValid];
}
- (void)dealloc
{
    [self.timer invalidate];
    self.timer = nil;

    NSLog(@"dealloc %s",__FUNCTION__);
}
@end

测试代码如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(10, 80, 40, 20)];
    lbl.textColor = [UIColor blackColor];
    lbl.font = [UIFont systemFontOfSize:14];
    lbl.backgroundColor = [UIColor redColor];
    lbl.text = @"0---";
    lbl.textAlignment = NSTextAlignmentCenter;
    [self.view addSubview:lbl];
    
    __weak typeof(self) weakSelf = self;
    [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"weakself %@ %@",weakSelf,lbl.text);
    }];
    [ZQQTimer timerWithHolder:self delay:0 TimeInterval:0 block:^(ZQQTimer *timer, NSInteger repeatCount) {
        NSLog(@"aaa %ld %@",repeatCount,timer);
        if (repeatCount == 5) {
            [timer invalidate];
        }
    }];

    
    [self startTimerDelay:0 timeInterval:1 block:^(ZQQTimer *timer, NSInteger repeatCount) {
        NSLog(@"%ld timer:%@",repeatCount,timer);
        lbl.text = [NSString stringWithFormat:@"%ld",repeatCount];
        if (repeatCount == 5) { // 可以手动取消timer的执行
            [timer invalidate];
        }
    }];
    
    [self startTimerWithBlock:^(ZQQTimer * timer, NSInteger repeatCount) {
        
    }];
}
- (void)dealloc
{
    NSLog(@"delloc %s",__FUNCTION__);
}

这上面的代码还用到了NSObject+ZQQBindData这个分类,这个之前博客里介绍过,主要利用下标语法,让任何NSObject都用有字典一样简便的读取变量的的功能,减少很多定义变量的麻烦

完整demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容