iOS学习笔记-134.RunLoop02——Runloop与线程

时间:2021-08-03 20:37:47

RunLoop02——Runloop与线程

一、通过 CFRunLoop.c 的 _CFRunLoopGet0 函数 发现端倪

我们首先下载到CFRunloop的源码

https://opensource.apple.com/source/CF/

找到我们 CFRunLoop.c 这个文件,搜索到 _CFRunLoopGet0 函数。

/获得runloop(创建runloop)

// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
// 创建字典
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
// 创建主线程对应的runloop
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
// 使用字典保存主线程-主线程对应的runloop
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);

if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}

// 从字典中获取子线程的runloop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
if (!loop) {
// 如果子线程的runloop不存在,那么就为该线程创建一个对应的runloop
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
// 把当前子线程和对应的runloop保存到字典中
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFUnlock(&loopsLock);
CFRelease(newLoop);
}
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}

我们可以看到,它会为线程创建一个 runloop 对象,然后存入到 字典中,这样它保持着一一对应的关系。


二、Runloop与线程的关系

1.Runloop和线程的关系:一个Runloop对应着一条唯一的线程。

Runloop对象是利用字典来进行存储,而且key是对应的线程Value为该线程对应的Runloop

问题:如何让子线程不死

回答:给这条子线程开启一个Runloop

2.Runloop的创建:主线程Runloop已经创建好了,子线程的runloop需要手动创建。

开一个子线程创建runloop,不是通过alloc init方法创建,而是直接通过调用

currentRunLoop 方法来创建,它本身是一个懒加载的

3.Runloop的生命周期:在第一次获取时创建,在线程结束时销毁


三、代码示例

//
// ViewController.m
// 03_UIView91_NSRunLoop简单运用
//
// Created by 杞文明 on 17/9/10.
// Copyright © 2017年 杞文明. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

//====================Foundation============================
// 1.获取主线程对应的runloop
NSRunLoop * mainRunLoop1 = [NSRunLoop mainRunLoop];

// 2.获取当前线程对应的runloop
NSRunLoop * currentRunLoop1 = [NSRunLoop currentRunLoop];

NSLog(@"%@---------%@",mainRunLoop1,currentRunLoop1);

//====================core foundation============================
// 1.获取主线程对应的runloop
CFRunLoopRef mainRunLoop2 = CFRunLoopGetMain();

// 2.获取当前线程对应的runloop
CFRunLoopRef currentRunLoop2 = CFRunLoopGetCurrent();

NSLog(@"%p---------%p",mainRunLoop2,currentRunLoop2);

//Runloop和线程的关系
//一一对应,主线程的runloop已经创建,但是子线程的需要手动创建
[[[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil] start];
}

//在runloop中有多个运行模式,但是runloop只能选择一种模式运行
//mode里面至少要有一个timer或者是source
-(void)run{
NSLog(@"=========run========");
//如何创建子线程对应的runLoop, currentRunLoop懒加载的
//现在还是不行的哦
NSLog(@"%@",[NSRunLoop currentRunLoop]);
NSLog(@"run--%@",[NSThread currentThread]);
}

@end

四、运行结果

[20640:123485] <CFRunLoop 0x60000016a5c0 [0x103794df0]>
......省略了很多输出......
---------<CFRunLoop 0x60000016a5c0 [0x103794df0]>
......省略了很多输出......
[20640:123485] 0x60000016a5c0---------0x60000016a5c0
[20640:123682] =========run========
[20640:123682] <CFRunLoop 0x60000016b880 [0x103794df0]>{wakeup port = 0x110b, stopped = false, ignoreWakeUps = true,
current mode = (none),
common modes = <CFBasicHash 0x600000051850 [0x103794df0]>{type = mutable set, count = 1,
entries =>
2 : <CFString 0x10376c920 [0x103794df0]>{contents = "kCFRunLoopDefaultMode"}
}
,
common mode items = (null),
modes = <CFBasicHash 0x600000051220 [0x103794df0]>{type = mutable set, count = 1,
entries =>
2 : <CFRunLoopMode 0x600000197830 [0x103794df0]>{name = kCFRunLoopDefaultMode, port set = 0xf07, queue = 0x60000016ba00, source = 0x6000001dfa40 (not fired), timer port = 0x650b,
sources0 = (null),
sources1 = (null),
observers = (null),
timers = (null),
currently 526736125 (7463726825342) / soft deadline in: 1.84467366e+10 sec (@ -1) / hard deadline in: 1.84467366e+10 sec (@ -1)
},

}
}
[20640:123682] run--<NSThread: 0x600000269880>{number = 3, name = (null)}

五、结果分析

我们发现 在主线程中 通过 foundation 中

// 1.获取主线程对应的runloop
NSRunLoop * mainRunLoop1 = [NSRunLoop mainRunLoop];

// 2.获取当前线程对应的runloop
NSRunLoop * currentRunLoop1 = [NSRunLoop currentRunLoop];

获取到的对象是同一个。


在主线程中 通过 core foundation 中

// 1.获取主线程对应的runloop
CFRunLoopRef mainRunLoop2 = CFRunLoopGetMain();

// 2.获取当前线程对应的runloop
CFRunLoopRef currentRunLoop2 = CFRunLoopGetCurrent();

获取到的对象是同一个。


core foundation 和 foundation 获取到的对象不是同一个的。这是因为,NSRunLoop 是 对 CFRunLoop 的再次封装.