iOS定时器相关实践

栏目: IOS · 发布时间: 5年前

内容简介:在iOS中,调用通过

在iOS中, NSTimer 是我们平常都会用到的定时器。所以我们有必要了解其相关的使用方法和需要注意的点。在我们创建 NSTimer 定时器是 timer 是强引用 target 的这样就形成了循环引用的问题,从而导致内存泄漏。要避免这样的问题我们可以使用一下方法:

  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();
    }
}
复制代码

调用

__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类的有关知识可以看链接的资料,这里就不再复述了。

@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定时器记录的时间相对要精确一些。而且不用考虑内存释放的问题。

以下是GCD实现基本的获取验证码的倒计时功能 直接上代码了

- (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创建定时器的基本方法就介绍完了


以上所述就是小编给大家介绍的《iOS定时器相关实践》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Database Design and Implementation

Database Design and Implementation

Edward Sciore / Wiley / 2008-10-24 / 1261.00 元

* Covering the traditional database system concepts from a systems perspective, this book addresses the functionality that database systems provide as well as what algorithms and design decisions will......一起来看看 《Database Design and Implementation》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具