Runloop

RunLoop详解

runloop的本质是一个对象,这个对象有一个入口函数,执行入口函数之后就会进入一个do while循环,循环的处理一些事情。

没有runloop的情况下,程序运行完成就会退出。
有runloop的时候程序运行完成的时候不会退出

runloop的作用

  1. 保持程序持续运行
  2. 处理app中的各种事件(触摸、定时器、performselector等)
  3. 节省cpu资源,提高程序性能(有事件的饿时候处理事件,没事件的时候休息,可以在main函数中直接返回1进行测试)

线程和runloop

  1. 线程和runloop是一一对应的关系(内部是一个字典,key为线程,value为runloop)
  2. 线程创建的时候,并没有创建runloop对象,runloop会在第一次获取的时候自动创建
  3. 主线程默认开启了runloop,子线程默认没有开启runloop

runloop的mode

  1. 一个runloop对应一个线程
  2. 一个runloop包含多个mode,每个mode包含若干个source、timer、observer
  3. source、timer、observer又叫modeitem,不同的mode下mode item 互不影响
  4. runloop运行过程中,只会选择一种模式运行
  5. 切换mode,程序退出当前mode,再重新制定mode执行

ModeItem

source0事件
  1. 触摸事件
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSLog(@"testRunloop"); //打断点可以看到堆栈信息中 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
}
  1. 自定义输入园source0
- (void)testAction:(UIButton *)sender
{
    NSThread *subThread = [[NSThread alloc]initWithBlock:^{
        CFRunLoopSourceContext context = {
            0,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL,
            NULL,
            schedule,
            cancel,
            perform
        };
        CFRunLoopSourceRef source0 = CFRunLoopSourceCreate(CFAllocatorGetDefault(), 0, &context);
        //有下面一行才能触发schedule回调
        CFRunLoopAddSource(CFRunLoopGetCurrent(), source0, kCFRunLoopDefaultMode);
        
        //有以下代码才能触发perform回调
        CFRunLoopSourceSignal(source0);
        CFRunLoopWakeUp(CFRunLoopGetCurrent());
        //触发cancel回调
        CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source0, kCFRunLoopDefaultMode);
        CFRelease(source0); //释放
    }];
    [subThread start];
}
void schedule(void *info, CFRunLoopRef rl, CFRunLoopMode mode)
{
    NSLog(@"currentThreed ===%s",__func__);
}

void cancel(void *info, CFRunLoopRef rl, CFRunLoopMode mode)
{
    NSLog(@"currentThreedMode ===%s",__func__);
}

void perform(void *info)
{
  NSLog(@"currentThreedInfo ===%s",__func__);
}
  1. performSelector:onThread:方法调用
[self performSelectorOnMainThread:@selector(animation:) withObject:nil waitUntilDone:NO];
- (void)animation:(id)sender
{
    NSLog(@"哈哈"); //此处打断点可以在堆栈中看到 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
}
source1
  1. 端口(NSPort)----

注意测试代码在模拟器上控制台能打印出来在手机上不行,原因是通过NSPort的port类方法创建出来的是它的子类对象NSMachPort,而Foundation框架给我们提供的NSPort的三个子类中NSMachPort和NSMessagePort只能用于同一台机器上的通信,NSSocketPort可以用于本地和远程通信。

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.mainPort = [NSPort port];
    self.mainPort.delegate = self;
    [[NSRunLoop currentRunLoop] addPort:self.mainPort forMode:NSDefaultRunLoopMode];

    NSThread *testThread = [[NSThread alloc]initWithBlock:^{
        self.subPort = [NSPort port];
        self.subPort.delegate = self;
        [[NSRunLoop currentRunLoop] addPort:self.subPort forMode:NSDefaultRunLoopMode];
        [[NSRunLoop currentRunLoop] run];
    }];
    [testThread setName:@"subThread"];
    [testThread start];
}

- (IBAction)testAction:(UIButton *)sender
{
    NSMutableArray *conmponents = [NSMutableArray array];
    [conmponents addObject:[@"fromMainThread" dataUsingEncoding:NSUTF8StringEncoding]];
    [self.subPort sendBeforeDate:[NSDate date] components:conmponents from:self.mainPort reserved:0];
}

- (void)handlePortMessage:(id)message
{
    NSMutableArray *array = [message valueForKey:@"components"]; //此处打断点可以在堆栈中看到__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__说明这是一个source1源
    NSString *password = [[NSString alloc]initWithData:[array  firstObject] encoding:NSUTF8StringEncoding];
    NSLog(@"thread ===%@\tpassword===%@", [NSThread currentThread], password);
    if (![[NSThread currentThread] isMainThread]) {
        NSMutableArray *conmponents = [NSMutableArray array];
        [conmponents addObject:[@"fromSubthread" dataUsingEncoding:NSUTF8StringEncoding]];
        [self.mainPort sendBeforeDate:[NSDate date] components:conmponents from:self.subPort reserved:0];
    }
}
计时源
  1. NStimer
  2. performSelector:withObject:afterDelay
[self performSelector:@selector(animation:) withObject:nil afterDelay:3];
- (void)animation:(id)sender
{
    NSLog(@"哈哈"); //此处打断点可以在堆栈中看到 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
}

你可能感兴趣的:(Runloop)