RunLoop詳解
runloop的本質(zhì)是一個(gè)對(duì)象攒钳,這個(gè)對(duì)象有一個(gè)入口函數(shù),執(zhí)行入口函數(shù)之后就會(huì)進(jìn)入一個(gè)do while循環(huán)雷滋,循環(huán)的處理一些事情中狂。
沒(méi)有runloop的情況下,程序運(yùn)行完成就會(huì)退出唤蔗。
有runloop的時(shí)候程序運(yùn)行完成的時(shí)候不會(huì)退出
runloop的作用
- 保持程序持續(xù)運(yùn)行
- 處理app中的各種事件(觸摸、定時(shí)器窟赏、performselector等)
- 節(jié)省cpu資源措译,提高程序性能(有事件的餓時(shí)候處理事件,沒(méi)事件的時(shí)候休息饰序,可以在main函數(shù)中直接返回1進(jìn)行測(cè)試)
線程和runloop
- 線程和runloop是一一對(duì)應(yīng)的關(guān)系(內(nèi)部是一個(gè)字典领虹,key為線程,value為runloop)
- 線程創(chuàng)建的時(shí)候求豫,并沒(méi)有創(chuàng)建runloop對(duì)象塌衰,runloop會(huì)在第一次獲取的時(shí)候自動(dòng)創(chuàng)建
- 主線程默認(rèn)開(kāi)啟了runloop,子線程默認(rèn)沒(méi)有開(kāi)啟runloop
runloop的mode
- 一個(gè)runloop對(duì)應(yīng)一個(gè)線程
- 一個(gè)runloop包含多個(gè)mode蝠嘉,每個(gè)mode包含若干個(gè)source最疆、timer、observer
- source蚤告、timer努酸、observer又叫modeitem,不同的mode下mode item 互不影響
- runloop運(yùn)行過(guò)程中杜恰,只會(huì)選擇一種模式運(yùn)行
- 切換mode获诈,程序退出當(dāng)前mode仍源,再重新制定mode執(zhí)行
ModeItem
source0事件
- 觸摸事件
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(@"testRunloop"); //打斷點(diǎn)可以看到堆棧信息中 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
}
- 自定義輸入園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);
//有下面一行才能觸發(fā)schedule回調(diào)
CFRunLoopAddSource(CFRunLoopGetCurrent(), source0, kCFRunLoopDefaultMode);
//有以下代碼才能觸發(fā)perform回調(diào)
CFRunLoopSourceSignal(source0);
CFRunLoopWakeUp(CFRunLoopGetCurrent());
//觸發(fā)cancel回調(diào)
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__);
}
- performSelector:onThread:方法調(diào)用
[self performSelectorOnMainThread:@selector(animation:) withObject:nil waitUntilDone:NO];
- (void)animation:(id)sender
{
NSLog(@"哈哈"); //此處打斷點(diǎn)可以在堆棧中看到 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
}
source1
- 端口(NSPort)----
注意測(cè)試代碼在模擬器上控制臺(tái)能打印出來(lái)在手機(jī)上不行,原因是通過(guò)NSPort的port類方法創(chuàng)建出來(lái)的是它的子類對(duì)象NSMachPort,而Foundation框架給我們提供的NSPort的三個(gè)子類中NSMachPort和NSMessagePort只能用于同一臺(tái)機(jī)器上的通信舔涎,NSSocketPort可以用于本地和遠(yuǎn)程通信笼踩。
- (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"]; //此處打斷點(diǎn)可以在堆棧中看到__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__說(shuō)明這是一個(gè)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];
}
}
計(jì)時(shí)源
- NStimer
- performSelector:withObject:afterDelay
[self performSelector:@selector(animation:) withObject:nil afterDelay:3];
- (void)animation:(id)sender
{
NSLog(@"哈哈"); //此處打斷點(diǎn)可以在堆棧中看到 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
}