WolkSense-Hexiwear/iOS/Hexiwear/CocoaMQTT/MSWeakTimer.m
2016-07-21 12:22:05 +02:00

218 lines
5.9 KiB
Objective-C

//
// MSWeakTimer.m
// MindSnacks
//
// Created by Javier Soto on 1/23/13.
//
//
#import "MSWeakTimer.h"
#import <libkern/OSAtomic.h>
#if !__has_feature(objc_arc)
#error MSWeakTimer is ARC only. Either turn on ARC for the project or use -fobjc-arc flag
#endif
#if OS_OBJECT_USE_OBJC
#define ms_gcd_property_qualifier strong
#define ms_release_gcd_object(object)
#else
#define ms_gcd_property_qualifier assign
#define ms_release_gcd_object(object) dispatch_release(object)
#endif
@interface MSWeakTimer ()
{
struct
{
uint32_t timerIsInvalidated;
} _timerFlags;
}
@property (nonatomic, assign) NSTimeInterval timeInterval;
@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, strong) id userInfo;
@property (nonatomic, assign) BOOL repeats;
@property (nonatomic, ms_gcd_property_qualifier) dispatch_queue_t privateSerialQueue;
@property (nonatomic, ms_gcd_property_qualifier) dispatch_source_t timer;
@end
@implementation MSWeakTimer
@synthesize tolerance = _tolerance;
- (id)initWithTimeInterval:(NSTimeInterval)timeInterval
target:(id)target
selector:(SEL)selector
userInfo:(id)userInfo
repeats:(BOOL)repeats
dispatchQueue:(dispatch_queue_t)dispatchQueue
{
NSParameterAssert(target);
NSParameterAssert(selector);
NSParameterAssert(dispatchQueue);
if ((self = [super init]))
{
self.timeInterval = timeInterval;
self.target = target;
self.selector = selector;
self.userInfo = userInfo;
self.repeats = repeats;
NSString *privateQueueName = [NSString stringWithFormat:@"com.mindsnacks.msweaktimer.%p", self];
self.privateSerialQueue = dispatch_queue_create([privateQueueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(self.privateSerialQueue, dispatchQueue);
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
0,
0,
self.privateSerialQueue);
}
return self;
}
- (id)init
{
return [self initWithTimeInterval:0
target:nil
selector:NULL
userInfo:nil
repeats:NO
dispatchQueue:nil];
}
+ (instancetype)scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval
target:(id)target
selector:(SEL)selector
userInfo:(id)userInfo
repeats:(BOOL)repeats
dispatchQueue:(dispatch_queue_t)dispatchQueue
{
MSWeakTimer *timer = [[self alloc] initWithTimeInterval:timeInterval
target:target
selector:selector
userInfo:userInfo
repeats:repeats
dispatchQueue:dispatchQueue];
[timer schedule];
return timer;
}
- (void)dealloc
{
[self invalidate];
ms_release_gcd_object(_privateSerialQueue);
}
- (NSString *)description
{
return [NSString stringWithFormat:@"<%@ %p> time_interval=%f target=%@ selector=%@ userInfo=%@ repeats=%d timer=%@",
NSStringFromClass([self class]),
self,
self.timeInterval,
self.target,
NSStringFromSelector(self.selector),
self.userInfo,
self.repeats,
self.timer];
}
#pragma mark -
- (void)setTolerance:(NSTimeInterval)tolerance
{
@synchronized(self)
{
if (tolerance != _tolerance)
{
_tolerance = tolerance;
[self resetTimerProperties];
}
}
}
- (NSTimeInterval)tolerance
{
@synchronized(self)
{
return _tolerance;
}
}
- (void)resetTimerProperties
{
int64_t intervalInNanoseconds = (int64_t)(self.timeInterval * NSEC_PER_SEC);
int64_t toleranceInNanoseconds = (int64_t)(self.tolerance * NSEC_PER_SEC);
dispatch_source_set_timer(self.timer,
dispatch_time(DISPATCH_TIME_NOW, intervalInNanoseconds),
(uint64_t)intervalInNanoseconds,
toleranceInNanoseconds
);
}
- (void)schedule
{
[self resetTimerProperties];
__weak MSWeakTimer *weakSelf = self;
dispatch_source_set_event_handler(self.timer, ^{
[weakSelf timerFired];
});
dispatch_resume(self.timer);
}
- (void)fire
{
[self timerFired];
}
- (void)invalidate
{
// We check with an atomic operation if it has already been invalidated. Ideally we would synchronize this on the private queue,
// but since we can't know the context from which this method will be called, dispatch_sync might cause a deadlock.
if (!OSAtomicTestAndSetBarrier(7, &_timerFlags.timerIsInvalidated))
{
dispatch_source_t timer = self.timer;
dispatch_async(self.privateSerialQueue, ^{
dispatch_source_cancel(timer);
ms_release_gcd_object(timer);
});
}
}
- (void)timerFired
{
// Checking attomatically if the timer has already been invalidated.
if (OSAtomicAnd32OrigBarrier(1, &_timerFlags.timerIsInvalidated))
{
return;
}
// We're not worried about this warning because the selector we're calling doesn't return a +1 object.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.target performSelector:self.selector withObject:self];
#pragma clang diagnostic pop
if (!self.repeats)
{
[self invalidate];
}
}
@end