天天看点

iOS 多线程 dispatch_queue NSRunLoop NSTimer NSPort

iOS中只有主线程的Runloop是默认开启的  

其他线程在完成任务后就会退出,想让线程完成任务后保持存活需要开启runloop

runloop本质上也就是一个死循环,不停的监听任务,所以开启runloop需要添加资源,常用的NSPort(端口监听 线程之间通信) 、NSTimer(定时器任务)

反过来Timer依赖runloop,在其他线程启动Timer

@property (nonatomic, strong) NSThread *thread;
@property (nonatomic, weak) NSTimer *timer;


self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(startTimer) object:nil];
[self.thread start];



- (void)startTimer
{
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
        NSLog(@"timer");
    }];
    //启动runloop
    [[NSRunLoop currentRunLoop] run];
}
           

下面用串行队列和RunLoop做一个有趣的实验

@property (nonatomic, strong) dispatch_queue_t queue;

//@property (nonatomic, weak) NSTimer *timer;
@property (nonatomic, strong) NSThread *thread;
@property (nonatomic, strong) NSPort *port;

self.queue = dispatch_queue_create("a_queue", DISPATCH_QUEUE_SERIAL);


dispatch_async(self.queue, ^{
    NSLog(@"111111");
// 添加timer和port效果是一样的
//  weakSelf.timer = [NSTimer scheduledTimerWithTimeInterval:10000.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
//       NSLog(@"timer");
//  }];
    weakSelf.thread = [NSThread currentThread];
    NSPort *port = [NSPort port];
    port.delegate = weakSelf;
    weakSelf.port = port;
    [[NSRunLoop currentRunLoop] addPort:port forMode:NSRunLoopCommonModes];
    [[NSRunLoop currentRunLoop] run];
     NSLog(@"222222");
});
dispatch_async(self.queue, ^{
    NSLog(@"aaaaaaa");
});
           

串行队列异步执行,会开启一个线程处理任务,任务执行打印111111

启动runloop后,相当于死循环,所以222222不会打印,也就是当前任务没法完成,串行队列会一直等待,

再添加的任务都不会执行,aaaaaa也不会打印

这会是个问题,这个queue将无法处理其他任务

接下来利用port结束runloop,向port发送一条Message

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
       [self.port sendBeforeDate:[NSDate date] msgid:100 components:nil from:nil reserved:0];
   });

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        dispatch_async(self.queue, ^{
            NSLog(@"bbbbbb");
        });
    });

- (void)handlePortMessage:(NSPortMessage *)message
{
    id msg = message;
     NSNumber *msgid =  [msg valueForKeyPath:@"msgid"];
    if ([msgid isEqual:@(100)]) {
        [[NSRunLoop currentRunLoop] removePort:self.port forMode:NSRunLoopCommonModes];
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), self.queue, ^{
            [[NSRunLoop currentRunLoop] addPort:self.port forMode:NSRunLoopCommonModes];
            [[NSRunLoop currentRunLoop] run];
            NSLog(@"333333");
        });
    }
}
           

收到message后停止runloop,会发现222222,aaaaaa打印

3秒后再次启动runloop,在这之后的任务bbbbbb将不会被执行