可控制生命周期的常驻线程

- (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
原文地址:https://www.cnblogs.com/yuxiaoyiyou/p/13162748.html