YYTimer.m 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. //
  2. // YYTimer.m
  3. // YYKit <https://github.com/ibireme/YYKit>
  4. //
  5. // Created by ibireme on 15/2/7.
  6. // Copyright (c) 2015 ibireme.
  7. //
  8. // This source code is licensed under the MIT-style license found in the
  9. // LICENSE file in the root directory of this source tree.
  10. //
  11. #import "YYTimer.h"
  12. #import <pthread.h>
  13. #define LOCK(...) dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER); \
  14. __VA_ARGS__; \
  15. dispatch_semaphore_signal(_lock);
  16. @implementation YYTimer {
  17. BOOL _valid;
  18. NSTimeInterval _timeInterval;
  19. BOOL _repeats;
  20. __weak id _target;
  21. SEL _selector;
  22. dispatch_source_t _source;
  23. dispatch_semaphore_t _lock;
  24. }
  25. + (YYTimer *)timerWithTimeInterval:(NSTimeInterval)interval
  26. target:(id)target
  27. selector:(SEL)selector
  28. repeats:(BOOL)repeats {
  29. return [[self alloc] initWithFireTime:interval interval:interval target:target selector:selector repeats:repeats];
  30. }
  31. - (instancetype)init {
  32. @throw [NSException exceptionWithName:@"YYTimer init error" reason:@"Use the designated initializer to init." userInfo:nil];
  33. return [self initWithFireTime:0 interval:0 target:self selector:@selector(invalidate) repeats:NO];
  34. }
  35. - (instancetype)initWithFireTime:(NSTimeInterval)start
  36. interval:(NSTimeInterval)interval
  37. target:(id)target
  38. selector:(SEL)selector
  39. repeats:(BOOL)repeats {
  40. self = [super init];
  41. _repeats = repeats;
  42. _timeInterval = interval;
  43. _valid = YES;
  44. _target = target;
  45. _selector = selector;
  46. __weak typeof(self) _self = self;
  47. _lock = dispatch_semaphore_create(1);
  48. _source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
  49. dispatch_source_set_timer(_source, dispatch_time(DISPATCH_TIME_NOW, (start * NSEC_PER_SEC)), (interval * NSEC_PER_SEC), 0);
  50. dispatch_source_set_event_handler(_source, ^{[_self fire];});
  51. dispatch_resume(_source);
  52. return self;
  53. }
  54. - (void)invalidate {
  55. dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
  56. if (_valid) {
  57. dispatch_source_cancel(_source);
  58. _source = NULL;
  59. _target = nil;
  60. _valid = NO;
  61. }
  62. dispatch_semaphore_signal(_lock);
  63. }
  64. - (void)fire {
  65. if (!_valid) return;
  66. #pragma clang diagnostic push
  67. #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
  68. dispatch_semaphore_wait(_lock, DISPATCH_TIME_FOREVER);
  69. id target = _target;
  70. if (!target) {
  71. dispatch_semaphore_signal(_lock);
  72. [self invalidate];
  73. } else {
  74. dispatch_semaphore_signal(_lock);
  75. [target performSelector:_selector withObject:self];
  76. if (!_repeats) {
  77. [self invalidate];
  78. }
  79. }
  80. #pragma clang diagnostic pop
  81. }
  82. - (BOOL)repeats {
  83. LOCK(BOOL repeat = _repeats); return repeat;
  84. }
  85. - (NSTimeInterval)timeInterval {
  86. LOCK(NSTimeInterval t = _timeInterval) return t;
  87. }
  88. - (BOOL)isValid {
  89. LOCK(BOOL valid = _valid) return valid;
  90. }
  91. - (void)dealloc {
  92. [self invalidate];
  93. }
  94. @end