- (void)testWorkThread {
self.thread = [AlwaysLiveThread new];
[self.thread startThisThread];
int count = 0;
int __block writeItem = 0;
int __block checkCount = 0;
while (count < 10) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(count * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.thread workJob:^{
NSLog(@"正在执行任务(%d)",count);
++writeItem;
}];
});
count++;
}
[self.thread waitConditions:^BOOL(BOOL *stop) {
*stop = (writeItem == 3);
if (*stop) {
NSLog(@"输出第3个数字了");
}
NSLog(@"当前已进行%d次条件检索",++checkCount);
return *stop;
} onThread:[NSThread mainThread] timegap:0.2 timeout:4.0 then:^(BOOL success) {
NSLog(@"条件等待:%@",success?@"成功":@"失败");
}];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(6.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.thread stopThisThread];
self.thread = nil;
});
}
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
typedef BOOL (^ThreadWaitConditionsBlock)(BOOL*);
typedef void (^ThreadConditionsCheckFinshBlock)(BOOL);
typedef void (^ThreadWillInsertJobBlock)(void);
@interface AlwaysLiveThread : NSObject
/**
@param block 加入到线程中进行处理的block
*/
- (void)workJob:(ThreadWillInsertJobBlock)block;
/**
@param block 加入到线程中进行处理的block
@param afterDelay 延迟执行的时间(与上一个方法实现的原理不一致)
*/
- (void)workJob:(ThreadWillInsertJobBlock)block afterDelay:(NSTimeInterval)afterDelay;
/**
启动这个线程,线程不启动所有方法都不会执行
*/
- (BOOL)startThisThread;
/**
结束这个线程,线程一结束所有的方法都不会继续执行了
*/
- (BOOL)stopThisThread;
/**
开启一个条件判断,条件符合或者等待超时执行then方法
@param conditions 判断条件
@param thread 回调的线程
@param timegap 检测的间隔(非精准触发)
@param timeout 最大等待时间
@param then 等待完成后执行的方法
*/
- (void)waitConditions:(ThreadWaitConditionsBlock)conditions onThread:(NSThread*)thread timegap:(NSTimeInterval)timegap timeout:(NSTimeInterval)timeout then:(ThreadConditionsCheckFinshBlock)then;
@end
NS_ASSUME_NONNULL_END
#import "AlwaysLiveThread.h"
@interface AlwaysLiveThread()
{
CFRunLoopSourceContext context;
CFRunLoopRef runLoop;
CFRunLoopSourceRef runLoopSource;
}
@property (nonatomic, strong) NSThread *workThread;
@property (nonatomic, assign) BOOL isStarted;
@property (nonatomic, assign) BOOL isStoped;
@end
static const NSString *kAfterDelayDurationKey = @"kAfterDelayDurationKey";
static const NSString *kFireTimestampKey = @"kFireTimestampKey";
static const NSString *kJobBlockKey = @"kJobBlockDurationKey";
static const NSString *kRunningThreadKey = @"kRunningThreadKey";
static const NSString *kBlockParamKey = @"kBlockParamKey";
@implementation AlwaysLiveThread
- (instancetype)init {
return [self initWithBlock:^{
}];
}
- (instancetype)initWithBlock:(ThreadWillInsertJobBlock)block {
self = [super init];
if (self) {
self.isStarted = NO;
self.isStoped = NO;
self.workThread = [[NSThread alloc] initWithTarget:self selector:@selector(startThreadByBlock:) object:block];
}
return self;
}
- (BOOL)startThisThread {
BOOL isStarted = self.isStarted;
if (!self.isStarted && !self.isStoped) {
[self.workThread start];
}
return (!self.isStoped && !isStarted);
}
- (void)startThreadByBlock:(ThreadWillInsertJobBlock)block {
if (!self.isStarted && !self.isStoped) {
!block?:block();
[self registerRunloopSourceThenRunning];
}
}
- (void)registerRunloopSourceThenRunning {
if (!self.isStarted && !self.isStoped) {
self.isStarted = YES;
runLoop = CFRunLoopGetCurrent();
runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context);
CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopDefaultMode);
CFRunLoopRun();
}
}
- (void)stopThisRunloopWhileDisableThread {
if (!self.isStoped && self.isStarted) {
CFRunLoopStop(runLoop);
CFRunLoopRemoveSource(runLoop, runLoopSource, kCFRunLoopDefaultMode);
CFRelease(runLoopSource);
CFRelease(&context);
self.isStoped = YES;
}
}
- (void)waitConditions:(ThreadWaitConditionsBlock)conditions onThread:(NSThread*)thread timegap:(NSTimeInterval)timegap timeout:(NSTimeInterval)timeout then:(ThreadConditionsCheckFinshBlock)then {
BOOL __block shouldStop = NO;
NSTimeInterval timestamp = CFAbsoluteTimeGetCurrent();
typeof(self) __weak weakself = self;
ThreadWillInsertJobBlock __block blockItem = nil;
ThreadWillInsertJobBlock block = ^{
typeof(weakself) __strong strongself = weakself;
NSTimeInterval fireTimestamp = CFAbsoluteTimeGetCurrent();
NSTimeInterval didTimeout = fireTimestamp - timestamp;
BOOL flag = conditions(&shouldStop);
if (shouldStop || didTimeout >= timeout) {
if (then) {
[strongself callbackJobInOtherThread:@{
kJobBlockKey : then,
kBlockParamKey : @(flag),
kRunningThreadKey : thread ? : [NSThread currentThread],
}];
}
} else {
[strongself workJob:blockItem afterDelay:MIN(timegap, (timeout - didTimeout))];
}
};
blockItem = block;
block();
}
- (void)callbackJobInOtherThread:(NSDictionary*)userInfo {
ThreadConditionsCheckFinshBlock then = userInfo[kJobBlockKey];
id param = userInfo[kBlockParamKey];
BOOL flag = [(NSNumber*)param boolValue];
NSThread *thread = userInfo[kRunningThreadKey] ? : [NSThread currentThread];
ThreadWillInsertJobBlock block = ^{
!then?:then(flag);
};
[self performSelector:@selector(doCallbackJobInOtherThread:) onThread:thread withObject:block waitUntilDone:NO];
}
- (void)doCallbackJobInOtherThread:(ThreadWillInsertJobBlock)block {
!block?:block();
}
- (void)workJob:(ThreadWillInsertJobBlock)block {
if (block) {
[self performSelector:@selector(doNewJob:) onThread:self.workThread withObject:block waitUntilDone:NO];
}
}
- (void)doNewJob:(ThreadWillInsertJobBlock)block {
!block?:block();
}
- (void)workJob:(ThreadWillInsertJobBlock)block afterDelay:(NSTimeInterval)afterDelay {
if (block) {
if (afterDelay <= 0.0) {
[self workJob:block];
} else {
NSTimeInterval timestamp = CFAbsoluteTimeGetCurrent();
NSTimeInterval fireTimestamp = timestamp + afterDelay;
[self performSelector:@selector(doNewJobAfterDelay:) onThread:self.workThread withObject:@{kJobBlockKey : block,
kFireTimestampKey : @(fireTimestamp),
} waitUntilDone:NO];
}
}
}
- (void)doNewJobAfterDelay:(NSDictionary*)userInfo {
NSTimeInterval fireTimestamp = [userInfo[kFireTimestampKey] doubleValue];
void (^block)(void) = userInfo[kJobBlockKey];
NSTimeInterval timestamp = CFAbsoluteTimeGetCurrent();
NSTimeInterval afterDelay = fireTimestamp - timestamp;
if (afterDelay <= 0.0) {
[self doNewJob:block];
} else {
[self performSelector:@selector(doNewJob:) withObject:block afterDelay:afterDelay];
}
}
- (BOOL)stopThisThread {
BOOL isStoped = self.isStoped;
if (self.isStarted && !self.isStoped) {
[self.class cancelPreviousPerformRequestsWithTarget:self];
/// 放在线程内执行,主要是避免跨线程操作异常(比如调用启动后立即停止,导致数据访问异常)
[self performSelector:@selector(stopThisRunloopWhileDisableThread) onThread:self.workThread withObject:nil waitUntilDone:NO];
}
return (self.isStarted && !isStoped);
}
- (void)dealloc {
#ifdef DEBUG
NSLog(@"任务线程%@被销毁",self);
#endif
}
@end