Runtime & Runloop

Runloop:应用篇

2020-03-15  本文已影响0人  码小菜
夜景

目录
一,解决performSelector:withObject:afterDelay:方法不执行的问题
二,解决界面滑动时NSTimer会停止的问题
三,启动方式
四,线程保活(一)
五,线程保活(二)

一,解决performSelector:withObject:afterDelay:方法不执行的问题

1,解决前

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"1---%@", [NSThread currentThread]);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"3---%@", [NSThread currentThread]);
        
        [self performSelector:@selector(perform)
                   withObject:nil
                   afterDelay:3.0];
        
        NSLog(@"4---%@", [NSThread currentThread]);
    });
    NSLog(@"2---%@", [NSThread currentThread]);
}

- (void)perform {
    NSLog(@"5---%@", [NSThread currentThread]);
}

// 打印
1---<NSThread: 0x600003cebb00>{number = 1, name = main}
2---<NSThread: 0x600003cebb00>{number = 1, name = main}
3---<NSThread: 0x600003cb1ec0>{number = 3, name = (null)}
4---<NSThread: 0x600003cb1ec0>{number = 3, name = (null)}

2,解决后

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"1---%@", [NSThread currentThread]);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"3---%@", [NSThread currentThread]);
        
        [self performSelector:@selector(perform)
                   withObject:nil
                   afterDelay:3.0];
        // 启动当前线程的runloop
        [[NSRunLoop currentRunLoop] run];
        
        NSLog(@"4---%@", [NSThread currentThread]);
    });
    NSLog(@"2---%@", [NSThread currentThread]);
}

// 打印
1---<NSThread: 0x60000154d940>{number = 1, name = main}
2---<NSThread: 0x60000154d940>{number = 1, name = main}
3---<NSThread: 0x600001522580>{number = 6, name = (null)}
5---<NSThread: 0x600001522580>{number = 6, name = (null)}
4---<NSThread: 0x600001522580>{number = 6, name = (null)}

3,说明

4,注意点

二,解决界面滑动时NSTimer会停止的问题

1,解决前

- (void)viewDidLoad {
    [super viewDidLoad];
    
    __block int count = 1;
    [NSTimer scheduledTimerWithTimeInterval:1.0
                                    repeats:YES
                                      block:^(NSTimer * _Nonnull timer) {
        NSLog(@"%d", count++);
    }];
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    NSLog(@"%s", __func__);
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
                  willDecelerate:(BOOL)decelerate {
    NSLog(@"%s", __func__);
}

// 打印
17:45:58.607770+0800 Demo[98743:17866588] 1
17:45:59.608341+0800 Demo[98743:17866588] 2
17:46:00.607569+0800 Demo[98743:17866588] 3
17:46:01.607681+0800 Demo[98743:17866588] 4
17:46:02.608093+0800 Demo[98743:17866588] 5
17:46:02.965690+0800 Demo[98743:17866588] -[ViewController scrollViewWillBeginDragging:]
17:46:12.026709+0800 Demo[98743:17866588] -[ViewController scrollViewDidEndDragging:willDecelerate:]
17:46:12.028420+0800 Demo[98743:17866588] 6
17:46:12.608795+0800 Demo[98743:17866588] 7
17:46:13.608181+0800 Demo[98743:17866588] 8
17:46:14.608112+0800 Demo[98743:17866588] 9
17:46:15.609020+0800 Demo[98743:17866588] 10

2,解决后

- (void)viewDidLoad {
    [super viewDidLoad];
    
    __block int count = 1;
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
                                            repeats:YES
                                              block:^(NSTimer * _Nonnull timer) {
        NSLog(@"%d", count++);
    }];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

// 打印
17:54:52.672257+0800 Demo[98815:17903655] 1
17:54:53.672473+0800 Demo[98815:17903655] 2
17:54:54.673453+0800 Demo[98815:17903655] 3
17:54:54.948438+0800 Demo[98815:17903655] -[ViewController scrollViewWillBeginDragging:]
17:54:55.673715+0800 Demo[98815:17903655] 4
17:54:56.675426+0800 Demo[98815:17903655] 5
17:54:57.675849+0800 Demo[98815:17903655] 6
17:54:58.676955+0800 Demo[98815:17903655] 7
17:54:59.214448+0800 Demo[98815:17903655] -[ViewController scrollViewDidEndDragging:willDecelerate:]
17:54:59.676629+0800 Demo[98815:17903655] 8
17:55:00.677990+0800 Demo[98815:17903655] 9
17:55:01.677777+0800 Demo[98815:17903655] 10

3,说明

三,启动方式

1,run:永不退出

[runloop run];
// 相当于
while (1) {
    [runloop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture];
}

2,runUntilDate::在指定时间退出

NSDate *limitDate = [NSDate dateWithTimeIntervalSinceNow:10];
[runloop runUntilDate:limitDate];
// 相当于
while ([NSDate.date compare:limitDate] == NSOrderedAscending) {
    [runloop runMode:NSDefaultRunLoopMode beforeDate:limitDate];
}

3,runMode:beforeDate::在指定时间或者完成一次循环后退出

[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
四,线程保活(一)

1,作用

2,无法持续的执行操作

@interface ViewController ()
@property (nonatomic, strong) NSThread *thread;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"initWithBlock---%@", [NSThread currentThread]);
    }];
    [self.thread start];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"%s", __func__);
    // 该操作无效
    [self performSelector:@selector(work)
                 onThread:self.thread
               withObject:nil
            waitUntilDone:NO];
}

- (void)work {
    NSLog(@"%s---%@", __func__, [NSThread currentThread]);
}
@end

// 打印
initWithBlock---<NSThread: 0x600000b75a80>{number = 6, name = (null)}
-[ViewController touchesBegan:withEvent:]

// block执行完,子线程的生命周期就结束了

3,启动runloop

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"begin---%@", [NSThread currentThread]);
        // 创建runloop
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        // 防止mode为空runloop退出
        [runloop addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
        // 启动runloop,runloop会让代码阻塞在此
        [runloop run];
        NSLog(@"end---%@", [NSThread currentThread]);
    }];
    [self.thread start];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    NSLog(@"%s", __func__);
    // 该操作有效
    [self performSelector:@selector(work)
                 onThread:self.thread
               withObject:nil
            waitUntilDone:NO];
}

// 打印
begin---<NSThread: 0x600001225ac0>{number = 7, name = (null)}
-[ViewController touchesBegan:withEvent:]
-[ViewController work]---<NSThread: 0x600001225ac0>{number = 7, name = (null)}

// 没有打印end,说明block没有执行完,子线程的生命周期还没有结束

4,点击按钮退出runloop

@interface ViewController ()
@property (nonatomic, strong) NSThread *thread;
@property (nonatomic, assign, getter=isStopped) BOOL stopped;
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    __weak typeof(self) weakSelf = self;
    self.thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"begin---%@", [NSThread currentThread]);
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        [runloop addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
        /* 
         1,run方法无法退出,所以改用此方法
         2,由于此方法完成一次循环后就退出了,所以需要加上while循环重新进入
         */
        while (!weakSelf.isStopped) {
            [runloop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture];
        }
        NSLog(@"end---%@", [NSThread currentThread]);
    }];
    [self.thread start];
}

- (IBAction)buttonClick {
    NSLog(@"%s", __func__);
    [self performSelector:@selector(stop)
                 onThread:self.thread
               withObject:nil
            waitUntilDone:NO];
}

- (void)stop {
    NSLog(@"%s---%@", __func__, [NSThread currentThread]);
    self.stopped = YES;
    // 退出当前线程的runloop
    CFRunLoopStop(CFRunLoopGetCurrent());
}

// 打印
begin---<NSThread: 0x60000207bec0>{number = 7, name = (null)}
-[ViewController touchesBegan:withEvent:]
-[ViewController work]---<NSThread: 0x60000207bec0>{number = 7, name = (null)}
-[ViewController buttonClick]
-[ViewController stop]---<NSThread: 0x60000207bec0>{number = 7, name = (null)}
end---<NSThread: 0x60000207bec0>{number = 7, name = (null)}

// 有打印end,说明block执行完了,子线程的生命周期也就结束了

5,控制器销毁时退出runloop

- (void)viewDidLoad {
    [super viewDidLoad];
    
    __weak typeof(self) weakSelf = self;
    self.thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"begin---%@", [NSThread currentThread]);
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        [runloop addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
        // 加上weakSelf非空判断,表示如果控制器销毁了,就不用再进入runloop了
        while (weakSelf && !weakSelf.isStopped) {
            [runloop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture];
        }
        NSLog(@"end---%@", [NSThread currentThread]);
    }];
    [self.thread start];
}

- (IBAction)buttonClick {
    NSLog(@"%s", __func__);
    [self performSelector:@selector(stop)
                 onThread:self.thread
               withObject:nil
    /*
     1,YES表示等待stop方法执行完再往下执行,NO表示不等待
     2,如果这里传NO的话,那么在执行stop方法时控制器已经销毁了,再使用self就会有问题
     */
            waitUntilDone:YES];
}

- (void)dealloc {
    NSLog(@"%s", __func__);
    [self buttonClick];
}

// 打印
begin---<NSThread: 0x6000032f6680>{number = 7, name = (null)}
-[ViewController touchesBegan:withEvent:]
-[ViewController work]---<NSThread: 0x6000032f6680>{number = 7, name = (null)}
-[ViewController dealloc]
-[ViewController buttonClick]
-[ViewController stop]---<NSThread: 0x6000032f6680>{number = 7, name = (null)}
end---<NSThread: 0x6000032f6680>{number = 7, name = (null)}

// 有打印end,说明block执行完了,子线程的生命周期也就结束了
五,线程保活(二)

1,封装

typedef void(^YJCustomThreadTask)(void);

@interface YJCustomThread : NSObject
- (void)run;
- (void)executeTask:(YJCustomThreadTask)task;
- (void)stop;
@end
@interface YJCustomThread ()
@property (nonatomic, strong) NSThread *thread;
@property (nonatomic, assign, getter=isStopped) BOOL stopped;
@end

@implementation YJCustomThread
- (instancetype)init {
    self = [super init];
    if (self) {
        __weak typeof(self) weakSelf = self;
        self.thread = [[NSThread alloc] initWithBlock:^{
            NSLog(@"begin---%@", [NSThread currentThread]);
            
            NSRunLoop *runloop = [NSRunLoop currentRunLoop];
            [runloop addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
            while (weakSelf && !weakSelf.isStopped) {
                [runloop runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture];
            }
            
            NSLog(@"end---%@", [NSThread currentThread]);
        }];
    }
    return self;
}

- (void)run {
    [self.thread start];
}

- (void)executeTask:(YJCustomThreadTask)task {
    if (!self.thread) return;
    
    [self performSelector:@selector(_execute:)
         onThread:self.thread
       withObject:task
    waitUntilDone:NO];
}

- (void)_execute:(YJCustomThreadTask)task {
    if (task) {
        task();
    }
}

- (void)stop {
    if (!self.thread) return;
    
    [self performSelector:@selector(_stop)
                 onThread:self.thread
               withObject:nil
            waitUntilDone:YES];
}

- (void)_stop {
    NSLog(@"stop---%@", [NSThread currentThread]);
    
    self.stopped = YES;
    CFRunLoopStop(CFRunLoopGetCurrent());
    self.thread = nil;
}
@end
@interface ViewController ()
@property (nonatomic, strong) YJCustomThread *customThread;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.customThread = [YJCustomThread new];
    [self.customThread run];
}

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

- (IBAction)buttonClick {
    [self.customThread stop];
}
@end

// 打印
begin---<NSThread: 0x600000502300>{number = 7, name = (null)}
execute---<NSThread: 0x600000502300>{number = 7, name = (null)}
stop---<NSThread: 0x600000502300>{number = 7, name = (null)}
end---<NSThread: 0x600000502300>{number = 7, name = (null)}

2,用CFRunLoopRef实现

- (instancetype)init {
    self = [super init];
    if (self) {
        __weak typeof(self) weakSelf = self;
        self.thread = [[NSThread alloc] initWithBlock:^{
            NSLog(@"begin---%@", [NSThread currentThread]);
            
            // 创建runloop
            CFRunLoopRef runloop = CFRunLoopGetCurrent();
            // 防止mode为空runloop退出
            CFRunLoopSourceContext context = {};
            CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
            CFRunLoopAddSource(runloop, source, kCFRunLoopDefaultMode);
            CFRelease(source);
            /*
             1,启动runloop
             2,第二个参数传无穷大,表示永不超时
             3,第三个参数传false,表示完成一次循环后不退出
             */
            CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);
            // 如果第三个参数传true,表示完成一次循环后退出,那么就需要加上while循环重新进入
//            while (weakSelf && !weakSelf.isStopped) {
//                CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
//            }
            
            NSLog(@"end---%@", [NSThread currentThread]);
        }];
    }
    return self;
}
上一篇下一篇

猜你喜欢

热点阅读