可控制生命周期的常驻线程
- (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