iOS RunLoop 常驻线程简化实现

1,828 阅读2分钟

RunLoop知识点

NSRunLoop 是 CFRunLoopRef 的包装类

NSRunLoopOC对象

CFRunLoopRefcore Foundation库中的定义

NSRunLoop的状态

// App运行的默认状态
NSDefaultRunLoopMode
// NSRunLoopCommonModes不是一种实际状态
// 而是NSDefaultRunLoopMode 和 UITrackingRunLoopMode 的集合
NSRunLoopCommonModes

CFRunLoop的状态

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),                    // 即将进入loop
    kCFRunLoopBeforeTimers = (1UL << 1),             // 即将处理timer
    kCFRunLoopBeforeSources = (1UL << 2),            // 即将处理source
    kCFRunLoopBeforeWaiting = (1UL << 5),            // 即将进入休眠
    kCFRunLoopAfterWaiting = (1UL << 6),             // 刚从休眠中唤醒
    kCFRunLoopExit = (1UL << 7),                     // 即将退出loop
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

RunLoop状态监听

//1.创建监听者
/*
     第一个参数:怎么分配存储空间
     第二个参数:要监听的状态 kCFRunLoopAllActivities 所有的状态
     第三个参数:时候持续监听
     第四个参数:优先级 总是传0
     第五个参数:当状态改变时候的回调
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
    /*
        kCFRunLoopEntry = (1UL << 0),        即将进入runloop
        kCFRunLoopBeforeTimers = (1UL << 1), 即将处理timer事件
        kCFRunLoopBeforeSources = (1UL << 2),即将处理source事件
        kCFRunLoopBeforeWaiting = (1UL << 5),即将进入睡眠
        kCFRunLoopAfterWaiting = (1UL << 6), 被唤醒
        kCFRunLoopExit = (1UL << 7),         runloop退出
        kCFRunLoopAllActivities = 0x0FFFFFFFU
    */
    switch (activity) {
        case kCFRunLoopEntry:
            NSLog(@"即将进入runloop");
            break;
        case kCFRunLoopBeforeTimers:
            NSLog(@"即将处理timer事件");
            break;
        case kCFRunLoopBeforeSources:
            NSLog(@"即将处理source事件");
            break;
        case kCFRunLoopBeforeWaiting:
            NSLog(@"即将进入睡眠");
            break;
        case kCFRunLoopAfterWaiting:
            NSLog(@"被唤醒");
            break;
        case kCFRunLoopExit:
            NSLog(@"runloop退出");
            break;

        default:
            break;
        }
    });

/*
    第一个参数:要监听哪个runloop
    第二个参数:观察者
    第三个参数:运行模式
    NSDefaultRunLoopMode == kCFRunLoopDefaultMode
    NSRunLoopCommonModes == kCFRunLoopCommonModes
*/
CFRunLoopAddObserver(CFRunLoopGetCurrent(),observer, kCFRunLoopDefaultMode);

// 销毁observer
CFRelease(observer);

OC版代码实现

//=======================.h===========================

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface PSKeepAliveThread : NSObject

/// 执行任务
/// @param task 任务block
- (void)executeTask:(void (^)(void))task;

/// 停止线程
- (void)stopThread;

@end

NS_ASSUME_NONNULL_END

//=======================.m===========================

#import "PSKeepAliveThread.h"

@interface PSKeepAliveThread ()

@property (nonatomic, strong) NSThread *innerThread;
@property (nonatomic, assign) BOOL isStop;
@property (nonatomic, assign) BOOL isRun;

@end

@implementation PSKeepAliveThread

- (instancetype)init {
    if (self = [super init]) {

        __weak typeof(self) weakSelf = self;
        self.innerThread = [[NSThread alloc] initWithBlock:^{
            // thread start

            //获取当前线程的runloop,并且给线程添加一个NSPort(source)
            [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];

            // 使用弱引用,保证isStop变量在设置为YES时,能退出循环
            while (weakSelf && !weakSelf.isStop) {
                // 当程序运行到这里时,如果没有需要执行的任务,则线程会进入休眠状态,并且不会继续执行while循环
                // 直到有任务要执行时,才会被唤醒
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            }

            // thread end
        }];

        // 启动线程
        [self run];
    }
    return self;
}

- (void)run {
    if (!self.isRun) {
        self.isRun = YES;
        [self.innerThread start];
    }
}

- (void)executeTask:(void (^)(void))task {
    // 如果线程已经结束了,或者任务为空的情况,不执行任务
    if (!self.innerThread || !task) return;

    //将任务放到线程中执行
    [self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}

- (void)__executeTask:(void (^)(void))task {
    task();
}

// 停止线程
- (void)stopThread {
    if (!self.innerThread) return;

    [self performSelector:@selector(__stopThread) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}

- (void)__stopThread {
    self.isStop = YES;
    self.isRun = NO;

    // 停止当前线程的Runloop
    CFRunLoopStop(CFRunLoopGetCurrent());

    self.innerThread = nil;
}

- (void)dealloc {
    //当前类释放时,停止线程
    [self stopThread];
}

@end

C语言版实现

#import "PSKeepAliveThread.h"

@interface PSKeepAliveThread ()

@property (nonatomic, strong) NSThread *innerThread;
@property (nonatomic, assign) BOOL isRun;

@end

@implementation PSKeepAliveThread

- (instancetype)init {
    if (self = [super init]) {

        self.innerThread = [[NSThread alloc] initWithBlock:^{
            // thread start

            // 创建上下文
            CFRunLoopSourceContext context = {0};

            // 创建source
            CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);

            // 向Runloop中添加source
            CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);

            // 销毁source
            CFRelease(source);

            // 运行runloop
            // 第三个参数returnAfterSourceHandled: 设置为true时,代表执行完任务之后就会退出当前loop
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);

            // thread end
        }];

        // 启动线程
        [self run];
    }
    return self;
}

- (void)run {
    if (!self.isRun) {
        self.isRun = YES;
        [self.innerThread start];
    }
}

- (void)executeTask:(void (^)(void))task {
    // 如果线程已经结束了,或者任务为空的情况,不执行任务
    if (!self.innerThread || !task) return;

    //将任务放到线程中执行
    [self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:task waitUntilDone:NO];
}

- (void)__executeTask:(void (^)(void))task {
    task();
}

// 停止线程
- (void)stopThread {
    if (!self.innerThread) return;

    [self performSelector:@selector(__stopThread) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}

- (void)__stopThread {
    self.isRun = NO;

    // 停止当前线程的Runloop
    CFRunLoopStop(CFRunLoopGetCurrent());

    self.innerThread = nil;
}

- (void)dealloc {
    //当前类释放时,停止线程
    [self stopThread];
}

@end

使用

#import "ViewController.h"

#import "PSKeepAliveThread.h"

@interface ViewController ()

@property (nonatomic, strong) PSKeepAliveThread *thread;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    self.thread = [[PSKeepAliveThread alloc] init];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.thread executeTask:^{
        NSLog(@"执行任务 %@", [NSThread currentThread]);
    }];
}

// 点击按钮,停止线程
- (IBAction)stopThreadAction:(UIButton *)sender {
    [self.thread stopThread];
}

- (void)dealloc {
    NSLog(@"%s", __func__);

    [self.thread stopThread];
}

@end