每条线程都有唯一的一个与之对应的RunLoop对象。
RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value。
线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建。
RunLoop会在线程结束时销毁。
主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop。
/*
从runLoop底层的源码也可以看出,线程和runloop是一一对应的,且是保存
在一个全局的CFDictionary中的,且第一次获取不到runloop会去创建它。
*/
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
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);
}
// RunLoop的底层结构
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */
__CFPort _wakeUpPort; // used for CFRunLoopWakeUp
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name;
Boolean _stopped;
char _padding[3];
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
由此可见,我们的RunLoop中主要的结构就2个成员变量_currentMode、_modes。
struct __CFRunLoop {
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
}
而_currentMode中的结构主要就是_sources0、_sources1、_observers、_time。简单的如下图所示:
runloop.png
CFRunLoopModeRef代表RunLoop的运行模式
一个RunLoop包含若干个Mode,每个Mode又包含若干个Source0、Source1、Timer、Observer。
RunLoop启动时只能选择其中一个Mode,作为currentMode。
如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入。
不同组的Source0/Source1/Timer/Observer能分隔开来,互不影响。
如果Mode里没有任何Source0/Source1/Timer/Observer,RunLoop会立马退出。
目前已知的Mode有5种:
- kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行。
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响。
- UIInitializationRunLoopMode:在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用。
- GSEventReceiveRunLoopMode:接受系统事件的内部 Mode,通常用不到
- kCFRunLoopCommonModes:这是一个占位用的Mode,不是一种真正的Mode。
例子1:那么来看看下面这道题,运行结果会是啥?打印1、3、2?错,结果是只打印1、3,2不会被打印,也就是说performSelector没有执行,那是为啥呢?
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:2];
NSLog(@"3");
});
-(void)test{
NSLog(@"2");
}
那要看一下performSelector:withObject:afterDelay的本质是干了啥。performSelector:withObject:afterDelay:其实就是在内部创建了一个NSTimer,然后会添加到当前线程的Runloop中。然鹅我们通过dispatch_async开辟的子线程,Runloop默认是没有开启的,所以它不会被执行。故,我们要手动的开启Runloop就行了。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"1");
[self performSelector:@selector(test) withObject:nil afterDelay:2];
[[NSRunLoop currentRunLoop] run]; // 开启子线程的NSRunLoop。
NSLog(@"3");
});
例子2:对应着上面的RunLoop的流程,那么我们可以自己来控制一个线程的生命周期么(自己控制线程的死活)?offcourse,show code:
#import "ViewController1.h"
#import "MyThread.h"
@interface ViewController1 ()
@property(nonatomic,strong)MyThread* myThread;
@property(nonatomic,assign)BOOL stop;
@end
@implementation ViewController1
- (void)viewDidLoad {
[super viewDidLoad];
[self createUI];
__weak typeof(self) weakSelf = self;
self.myThread = [[MyThread alloc]initWithBlock:^{
/*
让当前的子线程一直活下去,必须要开启该子线程的RunLoop,
且给runloop添加一个source/timer/port 等。不然线程执行一次就会自动销毁了。
*/
NSLog(@"----thread---begin");
[[NSRunLoop currentRunLoop]addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];
while (weakSelf && !weakSelf.stop) {
[[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
NSLog(@"----thread---end");
}];
[self.myThread start];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 点击屏幕,让当前子线程做事情
if (!self.myThread) return;
[self performSelector:@selector(threadDoSomeThing) onThread:self.myThread withObject:nil waitUntilDone:NO];
}
-(void)threadDoSomeThing{ // 当前子线程做的事情
NSLog(@"-------threadDoSomeThing-------%@",[NSThread currentThread]);
}
-(void)stopThread{ //点击页面按钮的时候去执行停止线程和RunLoop的函数
if (!self.myThread) return;
[self performSelector:@selector(stopMyThread) onThread:self.myThread withObject:nil waitUntilDone:YES];
}
-(void)stopMyThread{ // 停止线程和RunLoop,并把thread置为nil;
self.stop = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.myThread = nil;
}
-(void)dealloc{ // VC销毁的时候,线程和RunLoop也要停止和销毁
[self stopThread];
NSLog(@"ViewController1 dealloc");
}
-(void)createUI{ // 创建页面UI,页面上有个按钮,用于停止线程和RunLoop的
self.title = @"我的线程";
self.view.backgroundColor = [UIColor systemPinkColor];
UIButton* btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 200, 59, 30)];
[btn setTitle:@"停止" forState:UIControlStateNormal];
[btn addTarget: self action:@selector(stopThread) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn];
}
运行效果如下:
运行效果.gif
网友评论