手動(dòng)目錄
- RunLoop 6大響應(yīng)事件
- RunLoop 與線程的關(guān)系
- RunLoop狀態(tài)監(jiān)聽
- RunLoop 數(shù)據(jù)結(jié)構(gòu)
- RunLoop流程
- 如何進(jìn)行休眠的
- RunLoop 與autoreleasePool
- RunLoop 與GCD
- RunLoop 應(yīng)用
- 線程保活
什么是RunLoop令野?
RunLoop 是循環(huán)運(yùn)行铜幽,在程序運(yùn)行過(guò)程中玛歌,循環(huán)做一些事情。
RunLoop 是通過(guò)內(nèi)部的運(yùn)行循環(huán)搞乏,來(lái)對(duì)消息/事件進(jìn)行管理的一個(gè)對(duì)象季惯。
其作用是
1、保持程序的持續(xù)運(yùn)行
2蕴轨、處理APP中的各種事件(觸摸、定時(shí)器骇吭、performSelector)
3橙弱、節(jié)省cpu資源、提供程序的性能:該做事就做事燥狰,該休息就休息
RunLoop 6大響應(yīng)事件
- block:
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
- 調(diào)用Timer:
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
- 響應(yīng)source0:
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
- 響應(yīng)
source1:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
- GCD主隊(duì)列
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
- observer源:比如通知
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
當(dāng) RunLoop 進(jìn)行回調(diào)時(shí)棘脐,一般都是通過(guò)一個(gè)很長(zhǎng)的函數(shù)調(diào)用出去 (call out), 當(dāng)你在你的代碼中下斷點(diǎn)調(diào)試時(shí),通常能在調(diào)用棧上看到這些函數(shù)龙致。下面是這幾個(gè)函數(shù)的整理版本蛀缝,如果你在調(diào)用棧中看到這些長(zhǎng)函數(shù)名,在這里查找一下就能定位到具體的調(diào)用地點(diǎn)了:
可結(jié)合地步的【流程圖】來(lái)看
{
/// 1. 通知Observers目代,即將進(jìn)入RunLoop
/// 此處有Observer會(huì)創(chuàng)建AutoreleasePool: _objc_autoreleasePoolPush();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);
do {
/// 2. 通知 Observers: 即將觸發(fā) Timer 回調(diào)屈梁。
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers);
/// 3. 通知 Observers: 即將觸發(fā) Source (非基于port的,Source0) 回調(diào)。
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
/// 4. 觸發(fā) Source0 (非基于port的) 回調(diào)榛了。
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
/// 6. 通知Observers在讶,即將進(jìn)入休眠
/// 此處有Observer釋放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting);
/// 7. sleep to wait msg.
mach_msg() -> mach_msg_trap();
/// 8. 通知Observers,線程被喚醒
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting);
/// 9. 如果是被Timer喚醒的霜大,回調(diào)Timer
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer);
/// 9. 如果是被dispatch喚醒的构哺,執(zhí)行所有調(diào)用 dispatch_async 等方法放入main queue 的 block
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block);
/// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件喚醒了,處理這個(gè)事件
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);
} while (...);
/// 10. 通知Observers战坤,即將退出RunLoop
/// 此處有Observer釋放AutoreleasePool: _objc_autoreleasePoolPop();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit);
}
RunLoop 與線程的關(guān)系
一一對(duì)應(yīng)的關(guān)系遮婶。
- 在內(nèi)部,線程-runloop 在內(nèi)部以
key-value
的形式存儲(chǔ)在一個(gè)全局字典中 湖笨。- 當(dāng)然,子線程中中蹦骑,如果不主動(dòng)去獲取慈省,runloop是沒有的。主線程中的runloop是在main函數(shù)的 UIApplicationMain中創(chuàng)建的。
NSRunLoop
是對(duì) CFRunLoopRef
的包裝,
在代碼中边败,一般有這幾種寫法
NSRunLoop *mainLoop1 = [NSRunLoop mainRunLoop];
NSRunLoop *currentLoop1 = [NSRunLoop currentRunLoop];
CFRunLoopRef mainLoop2 = CFRunLoopGetMain();
CFRunLoopRef runloop2 = CFRunLoopGetCurrent();
底層都是C語(yǔ)言代碼袱衷,在追蹤源碼的時(shí)候,要用CFRunLoopGetMain
或者CFRunLoopGetCurrent
來(lái)追蹤
我們?cè)?a target="_blank">源碼中能找到這樣的代碼:(源碼不是工程笑窜,創(chuàng)建一個(gè)空工程致燥,將源碼拖進(jìn)去)
// 追蹤 CFRunLoopGetCurrent
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
static CFMutableDictionaryRef __CFRunLoops = NULL;
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);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
// 進(jìn)行綁定 dict[@"pthread_main_thread_np"] = mainLoop
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
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);
}
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存在一個(gè)字典
CFMutableDictionaryRef
,
字典中 以key-value 的形式存取排截,CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
類似于這樣
NSMutableDictionary *runloops = [NSMutableDictionary new];
runloops[@"祝線程"] = mainRunloop;
runloops[@"線程1"] = runloop1;
runloops[@"線程2"] = runloop2;
RunLoop狀態(tài)監(jiān)聽
RunLoop 有幾種狀態(tài)
enum CFRunLoopActivity {
kCFRunLoopEntry = (1 << 0), // 即將進(jìn)入Loop
kCFRunLoopBeforeTimers = (1 << 1), // 即將處理 Timer
kCFRunLoopBeforeSources = (1 << 2), // 即將處理 Source
kCFRunLoopBeforeWaiting = (1 << 5), // 即將進(jìn)入休眠
kCFRunLoopAfterWaiting = (1 << 6), // 剛從休眠中喚醒
kCFRunLoopExit = (1 << 7), // 即將退出Loop
kCFRunLoopAllActivities = 0x0FFFFFFFU // 包含上面所有狀態(tài)
};
RunLoop的狀態(tài)是可以添加監(jiān)聽來(lái)看看是怎樣的狀態(tài)
// 方法1: 指定 執(zhí)行方法的 方式
void observeRunLoopActicities(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"kCFRunLoopEntry");
break;
......
default:
break;
}
}
{
// 創(chuàng)建Observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, observeRunLoopActicities, NULL);
// 添加Observer到RunLoop中
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
// 釋放
CFRelease(observer);
}
// 方法2: block 方式
{
// 創(chuàng)建Observer
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopEntry - %@", mode);
CFRelease(mode);
break;
}
case kCFRunLoopExit: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopExit - %@", mode);
CFRelease(mode);
break;
}
default:
break;
}
});
// 添加Observer到RunLoop中
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
// 釋放
CFRelease(observer);
}
RunLoop 數(shù)據(jù)結(jié)構(gòu)
追蹤源碼 CFRunLoop
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;
};
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 */
};
__CFRunLoop 關(guān)鍵5個(gè)信息
_pthread
:
與之對(duì)應(yīng)的線程_commonModes
:_commonModeItems
:
在commonModes狀態(tài)下運(yùn)行的對(duì)象(例如Timer)_currentMode
:
在當(dāng)前l(fā)oop下運(yùn)行的mode(即使有多種mode嫌蚤,但同一時(shí)刻只能在一個(gè)mode下運(yùn)行)modes
:
運(yùn)行的所有模式----- 類似于CFMutableSetRef< CFRunLoopModeRef >
__CFRunLoopMode 關(guān)鍵的信息
_sources0
觸摸事件處理
performSelector:onThread:withObject:waitUntilDone:_sources1
基于Port的線程間的通訊
系統(tǒng)事件捕捉(比如點(diǎn)擊事件,由source1捕捉断傲,然后包裝之后交給source0處理)_observers
監(jiān)聽RunLoop的狀態(tài)
UI刷新(在BeforeWaiting的時(shí)候刷新)
AutoreleasePool_timers
NSTimer
performSelector:withObject:脱吱、 afterDelay:
RunLoopMode 類型
NSDefaultRunLoopMode
App的默認(rèn)ModeUITrackingRunLoopMode
滾動(dòng)的時(shí)候處于這個(gè)modeNSRunLoopCommonModes
這是一個(gè)占位用的Mode,不是一種真正的Mode认罩,它是上面2個(gè)mode的集合UIInitializationRunLoopMode
在剛啟動(dòng)App時(shí)進(jìn)入的第一個(gè)Mode箱蝠,啟動(dòng)完成后就不再使用GSEventReceiveRunLoopMode
接受系統(tǒng)事件的Mode,通常由系統(tǒng)去使用前三種mode 對(duì)外提供垦垂,后面的2中不能使用宦搬。
為什么 一個(gè)runloop里包含多個(gè)mode?
不同mode中的 source0劫拗、source1间校、timer、observe 能分割開來(lái)杨幼,互不影響撇簿。
RunLoop流程
流程問(wèn)題,從run開始
(內(nèi)部代碼太多差购,貼一份精簡(jiǎn)的代碼)
// CFRunLoopRun
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
__CFRunLoopLock(rl);
/// 首先根據(jù)modeName找到對(duì)應(yīng)mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
/// 通知 Observers: RunLoop 即將進(jìn)入 loop四瘫。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
/// 內(nèi)部函數(shù),進(jìn)入loop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
/// 通知 Observers: RunLoop 即將退出欲逃。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
/// 通知 Observers: 即將處理timer事件
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
/// 通知 Observers: 即將處理Source事件
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources)
/// 處理Blocks
__CFRunLoopDoBlocks(rl, rlm);
/// 處理sources0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
/// 處理sources0返回為YES
if (sourceHandledThisLoop) {
/// 處理Blocks
__CFRunLoopDoBlocks(rl, rlm);
}
/// 判斷有無(wú)端口消息(Source1)
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
/// 處理消息
goto handle_msg;
}
/// 通知 Observers: 即將進(jìn)入休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
/// 等待被喚醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
// user callouts now OK again
__CFRunLoopUnsetSleeping(rl);
/// 通知 Observers: 被喚醒找蜜,結(jié)束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:
if (被Timer喚醒) {
/// 處理Timers
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
} else if (被GCD喚醒) {
/// 處理gcd
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else if (被Source1喚醒) {
/// 被Source1喚醒稳析,處理Source1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
}
/// 處理block
__CFRunLoopDoBlocks(rl, rlm);
if (sourceHandledThisLoop && stopAfterHandle) {
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
retVal = kCFRunLoopRunFinished;
}
} while (0 == retVal);
return retVal;
}
1洗做、observers 進(jìn)入loop(entry)
2、通知observer 即將處理Timer(BeforeTimers)
3彰居、通知observer即將處理Sources (BeforeSources )
4诚纸、處理GCD
5、處理source0
- 5.1 如果source0 有GCD需要處理陈惰,就處理GCD
6畦徘、判斷有沒有端口消息(source1),有的話 繼續(xù)處理
7、通知 observe即將進(jìn)入休眠(BeforeWaiting)井辆,進(jìn)入休眠之后進(jìn)行事件監(jiān)聽关筒。(會(huì)一直卡在這直到被其他喚醒)
8、通知observe結(jié)束休眠(AfterWaiting)
- 8.1杯缺、Timer喚醒 : 處理Timer
- 8.2蒸播、GCD喚醒:處理GCD
- 8.3、source1喚醒:處理Source1
9萍肆、執(zhí)行GCD
10袍榆、根據(jù)之前的執(zhí)行結(jié)果 決定loop是繼續(xù)循環(huán) 還是退出
11、通知observe 退出(exit )匾鸥。
這里有一個(gè)點(diǎn)需要注意 :
能夠喚醒loop的有3個(gè):Timer
蜡塌、GCD
、Source1
勿负;
Source0不能直接喚醒loop
source0是用戶觸摸事件馏艾,實(shí)際上是有系統(tǒng)(Source1)捕捉到點(diǎn)擊事件,然后包裝之后交給source0處理
如何進(jìn)行休眠的
在用戶層奴愉,是無(wú)法達(dá)到休眠(等待而不執(zhí)行任何事物)的目的琅摩。通過(guò)內(nèi)核去操作。
以下內(nèi)容摘錄于:深入理解RunLoop
mach_msg() 函數(shù)實(shí)際上是調(diào)用了一個(gè) Mach 陷阱 (trap)锭硼,即函數(shù)mach_msg_trap()房资,陷阱這個(gè)概念在 Mach 中等同于系統(tǒng)調(diào)用。當(dāng)你在用戶態(tài)調(diào)用 mach_msg_trap() 時(shí)會(huì)觸發(fā)陷阱機(jī)制檀头,切換到內(nèi)核態(tài)轰异;內(nèi)核態(tài)中內(nèi)核實(shí)現(xiàn)的 mach_msg() 函數(shù)會(huì)完成實(shí)際的工作,如下圖:
RunLoop 與autoreleasePool
- 即將進(jìn)入Loop(kCFRunLoopEntry)暑始,其回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動(dòng)釋放池搭独。其order是-2147483647,優(yōu)先級(jí)最高廊镜,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前牙肝。
- 準(zhǔn)備進(jìn)入休眠(kCFRunLoopBeforeWaiting),此時(shí)調(diào)用 _objc_autoreleasePoolPop() 對(duì)釋放池里面的 對(duì)象進(jìn)行release操作
- 即將退出Loop(kCFRunLoopExit)此時(shí)調(diào)用 _objc_autoreleasePoolPop()釋放自動(dòng)釋放池嗤朴。這個(gè) Observer的order是2147483647配椭,確保池子釋放在所有回調(diào)之后。
RunLoop 與GCD
RunLoop與GCD的關(guān)系與2點(diǎn):
1雹姊、在do..while 循環(huán)中股缸,判斷runloop有沒有超時(shí),用的是GCD的source_timer
2吱雏、當(dāng)回主線程敦姻,也就是 GCD使用main_queue的時(shí)候寺酪,會(huì)向runloop發(fā)送一個(gè)_MAIN_DISPATCH_QUEUE的消息來(lái)喚醒runloop。這也是為什么子線程不能刷UI的原因之一替劈。
- 子線程 的GCD不能做UI的刷新等操作。是因?yàn)樵谧泳€程中得滤,無(wú)法喚醒runloop進(jìn)行UI刷新陨献。
我們做以下 操作:
執(zhí)行這段代碼,雖然設(shè)置backgroundColor的時(shí)候懂更,顏色沒有發(fā)生變化眨业,但是當(dāng)dispatch_after執(zhí)行,或者主動(dòng)執(zhí)行runloop run 操作 的時(shí)候沮协,顏色成功的改變了龄捡。- (void)task8 { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ self.view.backgroundColor = [UIColor redColor]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"1"); }); }); // [[NSRunLoop currentRunLoop] run]; }
RunLoop 應(yīng)用
線程保活
上面說(shuō)了runloop的原理慷暂。
那么就知道為什么有時(shí)候NSTimer不起效了聘殖。
RunLoop還能干什么? --線程毙腥穑活
一般來(lái)說(shuō)奸腺,我們?cè)谧泳€程里面執(zhí)行完任務(wù),子線程就會(huì)退出血久,但是突照,有時(shí)候我們需要在子線程多次執(zhí)行任務(wù),就需要線程一直不退出來(lái)節(jié)省消耗氧吐。
我們可以這樣做讹蘑。
------向子線程中 添加一個(gè)port (相當(dāng)于向runloop中 添加了一個(gè) source1)保證runloop中一直有事件要處理。
代碼如下:
@property (nonatomic, strong) JEThread *thread;
@property (nonatomic, assign) BOOL stopped;
{
__weak typeof(self) weakSelf = self;
_thread = [[JEThread alloc] initWithBlock:^{ // iOS 10.0
[[NSRunLoop currentRunLoop ] addPort:[NSPort new] forMode:NSDefaultRunLoopMode];
// [[NSRunLoop currentRunLoop] run]; // 這個(gè)需要注意筑舅,內(nèi)部會(huì)無(wú)限循環(huán)run座慰, 不能手動(dòng)停止
while (weakSelf && !weakSelf.stopped) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
NSLog(@"-------end-------");
}];
[self.thread start];
}
- (void)stop {
if (!self.thread) return;
// 在子線程調(diào)用stop
[self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}
// 用于停止子線程的RunLoop
- (void)stopThread
{
// 設(shè)置標(biāo)記為NO
self.stopped = YES;
// 停止RunLoop
CFRunLoopStop(CFRunLoopGetCurrent());
NSLog(@"%s %@", __func__, [NSThread currentThread]);
self.thread = nil;
}
- (void)dealloc {
[self stop];
NSLog(@"vc dealloc");
}
// 在子線程里處理自己的事情
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
if (!self.thread) return;
[self performSelector:@selector(doSomeThings) onThread:self.thread withObject:nil waitUntilDone:NO];
}
- (void)doSomeThings {
NSLog(@"1");
}
這里面有幾個(gè)點(diǎn)需要注意:
1、為什么沒有用
[runloop run]
而是用runMode:beforeDate
?
因?yàn)閞un 內(nèi)部進(jìn)行循環(huán)調(diào)用runMode:beforeDate
豁翎,我們需要自己打破這個(gè)循環(huán)角骤,所以我們要進(jìn)行自己的 while 條件循環(huán)。2心剥、
stop
方法里面 的 waitUntilDone 必須為YES邦尊?
考慮到可能會(huì)在dealloc里面調(diào)用stop,如果不等待优烧,就會(huì)先釋放了self蝉揍,這個(gè)時(shí)候再進(jìn)行 self.stopped 就是無(wú)效的。
參考 文章:
深入理解RunLoop ????