Runloop
運行循環(huán),在程序運行過程中循環(huán)做一些事情.
應(yīng)用范疇:
- 保持程序的持續(xù)運行
- 定時器 performSelector
- GCD Async Main Queue
- 事件響應(yīng) 手勢識別 界面刷新
- 網(wǎng)絡(luò)請求
- AutoreleasePool
Runloop與線程
- 每條線程都有唯一的一個與之對應(yīng)的
Runloop
對象. -
Runloop
保存在一個全局的字典里面,線程最為.key
,runloop
最為value
. - 第一次獲取的時候自動創(chuàng)建.
-
Runloop
線程結(jié)束時候銷毀. - 主線程的
Runloop
自動創(chuàng)建,子線程默認沒有開啟Runloop
.
獲取RunLoop對象
- Foundation:NSRunloop
- Core Foundation:CFRunloopRef
NSRunLoop *runloop;
CFRunLoopRef runloop2;
runloop = [NSRunLoop currentRunLoop];
runloop2 = CFRunLoopGetCurrent();
[NSRunLoop mainRunLoop];//主線程runloop
RunLoop相關(guān)的類
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimeRef
CFRunLoopObserverRef
CFRunLoopRef
底層結(jié)構(gòu)
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
...
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;//集合包含各種CFRunLoopModeRef對象
...
};
CFRunLoopModeRef
底層結(jié)構(gòu)
struct __CFRunLoopMode {
...
CFStringRef _name;
CFMutableSetRef _sources0;//(集合包含CFRunLoopSourceRef)
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;//數(shù)組包含CFRunLoopObserverRef
CFMutableArrayRef _timers;//數(shù)組包含CFRunLoopTimeRef
...
};
CFRunLoopModeRef
常見2種mode
-
kCFRunLoopDefaultModel
(NSDefaultRunLoopMode
),App默認mode,通常主線程處于這個mode下. -
UITrackingRunLoopMode
:界面跟蹤Mode,用于ScrollView追蹤滑動觸摸,保證界面滑動時候不受其他mode影響.
source0
- 觸摸事件處理
performSelectoreThread
source1
- 基于
port
的線程間通信 - 系統(tǒng)事件捕捉
timers
NSTimer
performSelector: withObject: afterDelay:
observers
- 監(jiān)聽
runloop
狀態(tài) UI刷新
CFRunLoopObserverRef
有幾種模式
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSRunLoop *runloop;
CFRunLoopRef runloop2;
runloop = [NSRunLoop currentRunLoop];
runloop2 = CFRunLoopGetCurrent();
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, MyCFRunLoopObserverCallBack, NULL);
CFRunLoopAddObserver(runloop2, observer, kCFRunLoopCommonModes);
CFRelease(observer);
}
/*
kCFRunLoopEntry = (1UL << 0),
kCFRunLoopBeforeTimers = (1UL << 1),
kCFRunLoopBeforeSources = (1UL << 2),
kCFRunLoopBeforeWaiting = (1UL << 5),
kCFRunLoopAfterWaiting = (1UL << 6),
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
*/
void MyCFRunLoopObserverCallBack(CFRunLoopObserverRef observer,
CFRunLoopActivity activity,
void *info)
{
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"kCFRunLoopEntry");
break;
// case kCFRunLoopBeforeTimers:
// NSLog(@"kCFRunLoopBeforeTimers");
// break;
// case kCFRunLoopBeforeSources:
// NSLog(@"kCFRunLoopBeforeSources");
// break;
// case kCFRunLoopBeforeWaiting:
// NSLog(@"kCFRunLoopBeforeWaiting");
// break;
// case kCFRunLoopAfterWaiting:
// NSLog(@"kCFRunLoopAfterWaiting");
// break;
case kCFRunLoopExit:
NSLog(@"kCFRunLoopExit");
break;
default:
break;
}
}
-
CFRunLoopModeRef
代表RunLoop
的運行模式. - 一個
RunLoop
包含若干個Mode
,每個Mode
又包含若干個/sources0/sources1/observers/timers
. -
RunLoop
啟動時候只能選擇一個mode
,作為currentMode
. - 切換
Mode
,只能退出當(dāng)前Loop
,再重新選擇mode
.
流程圖
實際應(yīng)用
- 控制線程聲明周期(線程保活)
- 解決NSTimer在滑動時候停止工作
- 監(jiān)控應(yīng)用卡頓
- 性能優(yōu)化
線程保活
@interface ViewController ()
@property (nonatomic,strong) MyThread *thread;
@property (nonatomic,assign) BOOL isStop;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
if (self.isStop) {
return;
}
[self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)test{
NSLog(@"test");
}
- (IBAction)stopXib:(id)sender {
if (self.isStop) {
return;
}
[self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}
- (void)stopThread{
self.isStop = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (MyThread *)thread{
if (!_thread) {
__weak typeof(self) weakSelf = self;
_thread = [[MyThread alloc]initWithBlock:^{
NSLog(@"---begin----%@",[NSThread currentThread]);
[[NSRunLoop currentRunLoop]addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
// [[NSRunLoop currentRunLoop]run];
while (weakSelf && !weakSelf.isStop) {
[[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate: [NSDate distantFuture]];
}
NSLog(@"---end----%@",[NSThread currentThread]);
}];
}
return _thread;
}
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
}
- (void)dealloc{
NSLog(@"%s",__func__);
[self stopXib:nil];
}
接口封裝
.h
typedef void(^MyTaskBlock)(void);
@interface MyPermenantThread : NSObject
- (void)run;
- (void)executeTask:(MyTaskBlock)block;
- (void)stop;
@end
.m
@interface MyPermenantThread()
@property (nonatomic,strong) MyThread *innerThread;
@property (nonatomic,assign,getter=isStopped) BOOL stopped;
@end
@implementation MyPermenantThread
- (instancetype)init
{
self = [super init];
if (self) {
__weak typeof (self) weakSelf = self;
self.innerThread = [[MyThread alloc]initWithBlock:^{
[[NSRunLoop currentRunLoop]addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
while (weakSelf && !weakSelf.isStopped) {
[[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
};
}];
}
return self;
}
- (void)run{
if (!self.innerThread) {
return;
}
[self.innerThread start];
}
- (void)executeTask:(MyTaskBlock)block{
if (!self.innerThread || !block) {
return;
}
[self performSelector:@selector(__executeTask:) onThread:self.innerThread withObject:block waitUntilDone:NO];
}
- (void)stop{
if (!self.innerThread) {
return;
}
[self performSelector:@selector(__stop) onThread:self.innerThread withObject:nil waitUntilDone:YES];
}
- (void)dealloc{
[self stop];
}
#pragma mark -- private methods
- (void)__stop{
self.stopped = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.innerThread = nil;
}
- (void)__executeTask:(MyTaskBlock)block
{
block();
}
@end
c語言實現(xiàn)
NSLog(@"begin");
CFRunLoopSourceContext context = {0} ;
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
while (weakSelf && !weakSelf.isStopped) {
//第三個參數(shù) 執(zhí)行完退出YES.
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
}
CFRelease(source);
NSLog(@"end");