文章目錄
1. 簡介
iOS 中負責程序運行循環,在程序運行過程中循環做一些事情;保持程序的運行,程序運行時會在main函數中創建一個runloop,負責主線程的持續運行;及處理app中的各種事件的響應,例如:NSTimer,UITouch等;在程序需要處理事件時候runloop就喚起線程,當不許處理事件的時候線程進入休眠狀態,這樣可以節省線程資源。
在OC中每一個線程都有唯一的一個Runloop對象;Runloop對象保存在一個全局的dictionary裏,線程作爲鍵Runloop作爲值;線程剛創建的時候是沒有runloop,當第一次獲取的時候創建;mian線程的Runloop程序運行時已經獲取了。
2. Runloop 對象獲取
源碼可以在蘋果開源網站下載,
2.1 get方法
在源碼裏找到CFRunLoop.c
文件,在該文件裏搜索 CFRunloopGetCurrent()方法;該方法裏調用了_CFRunLoopGet0(pthread_self());
,下面看下該方法實現:
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops) {//如果不存在,則創建一個保存Runloop對象的字典,
__CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
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);
}
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;
}
2.2 runLoop 相關類及執行狀態
常用相關的類主要有5個:CFRunLoopRef、CFRunLoopModelRef、CFRunLoopSourceRef、CFRunLoopTimerRef、CFRunLoopObseverRef;
CFRunLoop在公開的源碼裏的實現是:
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;//當前的模式
CFMutableSetRef _modes;//所有的運行模式
};
struct __CFRunLoopMode {
CFRuntimeBase _base;
CFStringRef _name;
CFMutableSetRef _sources0;//觸摸事件,performSelector:onThread:withobject:
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
};
從上述源碼中,可以看出一個RunLooop可以包含多個mode,每一個mode又包含多個_sources0、_sources1、_observers、_timers;當前是那個mode取決於_currentMode
,同時也只能存在一種模式可以作爲_currentMode,當需要切換到另外一種模式的時候會退出當前的mode;常用的有兩種mode:kCFRunLoopDefaultMode
主線程一般都是運行在該mode下、UITrackingRunLoopMode
界面跟蹤mode,界面滑動觸摸,保證界面滑動時候不受其他mode的影響;
看一下觸摸事件的函數調用堆棧:
接下來通過一段代碼驗證一下模式的切換:
CFRunLoopObserverRef obRef = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities,YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:{
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopEntry %@",mode);
CFRelease(mode);
}
break;
case kCFRunLoopBeforeTimers: {
break;
}
case kCFRunLoopBeforeSources: {
break;
}
case kCFRunLoopBeforeWaiting: {
break;
}
case kCFRunLoopAfterWaiting: {
break;
}
case kCFRunLoopExit: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopExit %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopAllActivities: {
break;
}
}
});
// CFRunLoopObserverRef obRef = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
//
// }, NULL);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), obRef, kCFRunLoopCommonModes);
CFRelease(obRef);
滑動view後的輸出:
3. RunLoop 運行邏輯
以下部分內容來自iOS底層原理總結 - RunLoop
3.1 RunLoop 調用過程
RunLoop 在CoreFoundation框架中可以添加消息監聽
CFRunLoopObserverRef obRef = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities,YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:{
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopEntry 進入 %@",mode);
CFRelease(mode);
}
break;
case kCFRunLoopBeforeTimers: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopBeforeTimers 處理timer %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopBeforeSources: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopBeforeSources 處理source %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopBeforeWaiting: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopBeforeWaiting 開始進入休息 %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopAfterWaiting: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopAfterWaiting 已經恢復 %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopExit: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopExit 退出了 %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopAllActivities: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopAllActivities %@",mode);
CFRelease(mode);
break;
}
}
});
// CFRunLoopObserverRef obRef = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
//
// }, NULL);
CFRunLoopAddObserver(CFRunLoopGetCurrent(), obRef, kCFRunLoopCommonModes);
CFRelease(obRef);
控制檯輸出,部分代碼精簡:
2019-09-25 09:06:58.969802+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 處理timer kCFRunLoopDefaultMode
2019-09-25 09:06:58.970439+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 處理source kCFRunLoopDefaultMode
2019-09-25 09:06:58.970643+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 處理timer kCFRunLoopDefaultMode
2019-09-25 09:06:58.970760+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 處理source kCFRunLoopDefaultMode
2019-09-25 09:06:58.970909+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 處理timer kCFRunLoopDefaultMode
2019-09-25 09:09:00.007058+0800 testRunLoop[5136:43000] kCFRunLoopBeforeWaiting 開始進入休息 kCFRunLoopDefaultMode
2019-09-25 09:10:00.003054+0800 testRunLoop[5136:43000] kCFRunLoopAfterWaiting 已經恢復 kCFRunLoopDefaultMode
2019-09-25 09:10:00.006223+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 處理timer kCFRunLoopDefaultMode
2019-09-25 09:10:00.006520+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 處理source kCFRunLoopDefaultMode
2019-09-25 09:10:00.006804+0800 testRunLoop[5136:43000] kCFRunLoopBeforeWaiting 開始進入休息 kCFRunLoopDefaultMode
2019-09-25 09:11:00.002788+0800 testRunLoop[5136:43000] kCFRunLoopAfterWaiting 已經恢復 kCFRunLoopDefaultMode
2019-09-25 09:11:00.005685+0800 testRunLoop[5136:43000] kCFRunLoopBeforeTimers 處理timer kCFRunLoopDefaultMode
2019-09-25 09:11:00.005937+0800 testRunLoop[5136:43000] kCFRunLoopBeforeSources 處理source kCFRunLoopDefaultMode
2019-09-25 09:11:00.006132+0800 testRunLoop[5136:43000] kCFRunLoopBeforeWaiting 開始進入休息 kCFRunLoopDefaultMode
3.2 源碼簡介
// 共外部調用的公開的CFRunLoopRun方法,其內部會調用CFRunLoopRunSpecific
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
// 經過精簡的 CFRunLoopRunSpecific 函數代碼,其內部會調用__CFRunLoopRun函數
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
// 通知Observers : 進入Loop
// __CFRunLoopDoObservers內部會調用 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
函數
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 核心的Loop邏輯
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知Observers : 退出Loop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
// 精簡後的 __CFRunLoopRun函數,保留了主要代碼
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
// 通知Observers:即將處理Timers
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
// 通知Observers:即將處理Sources
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// 處理Blocks
__CFRunLoopDoBlocks(rl, rlm);
// 處理Sources0
if (__CFRunLoopDoSources0(rl, rlm, stopAfterHandle)) {
// 處理Blocks
__CFRunLoopDoBlocks(rl, rlm);
}
// 如果有Sources1,就跳轉到handle_msg標記處
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
// 通知Observers:即將休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
// 進入休眠,等待其他消息喚醒
__CFRunLoopSetSleeping(rl);
__CFPortSetInsert(dispatchPort, waitSet);
do {
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
} while (1);
// 醒來
__CFPortSetRemove(dispatchPort, waitSet);
__CFRunLoopUnsetSleeping(rl);
// 通知Observers:已經喚醒
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg: // 看看是誰喚醒了RunLoop,進行相應的處理
if (被Timer喚醒的) {
// 處理Timer
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
}
else if (被GCD喚醒的) {
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else { // 被Sources1喚醒的
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply);
}
// 執行Blocks
__CFRunLoopDoBlocks(rl, rlm);
// 根據之前的執行結果,來決定怎麼做,爲retVal賦相應的值
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;
}
3.3 對timer的影響
static int timerCount = 0;
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
// NSLog(@"timerCount = %d",self->_timerCount++);
NSLog(@"timerCount = %d",timerCount++);
}];
//NSDefaultRunLoopMode kCFRunLoopCommonModes
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
從控制檯的時間戳裏可以看出,屏幕滑動會對timer的影響;將NSDefaultRunLoopMode
換成kCFRunLoopCommonModes
屏幕的滑動就不會對timer產生影響;
4 子線程中對NSTimer的影響
4.1 子線程如何使用NSTimer,通過上面的分析知道:主線程runLoop成程序啓動後會運行,而子線程的runLoop不主動調用是沒有的,而且即使主動調用子線程的runloop是默認不運行的,因此子線程的timer運行,一般我們按照如下方式處理:
#import "TestTimer.h"
@interface TestTimer ()
@property(nonatomic,strong)NSTimer *timer;
@property(nonatomic,strong)dispatch_queue_t que;
@end
@implementation TestTimer
-(void)run{
static int timerCount = 0;
self.que = dispatch_queue_create("ddddd", DISPATCH_QUEUE_CONCURRENT);
__weak typeof(self) weakSelf = self;
dispatch_async(_que, ^{
__weak typeof(weakSelf) strongSelf = weakSelf;
CFRunLoopObserverRef obRef = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities,YES,0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:{
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopEntry 進入 %@",mode);
CFRelease(mode);
}
break;
case kCFRunLoopBeforeTimers: {
// CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
// NSLog(@"kCFRunLoopBeforeTimers 處理timer %@",mode);
// CFRelease(mode);
break;
}
case kCFRunLoopBeforeSources: {
// CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
// NSLog(@"kCFRunLoopBeforeSources 處理source %@",mode);
// CFRelease(mode);
break;
}
case kCFRunLoopBeforeWaiting: {
// CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
// NSLog(@"kCFRunLoopBeforeWaiting 開始進入休息 %@",mode);
// CFRelease(mode);
break;
}
case kCFRunLoopAfterWaiting: {
// CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
// NSLog(@"kCFRunLoopAfterWaiting 已經恢復 %@",mode);
// CFRelease(mode);
break;
}
case kCFRunLoopExit: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopExit 退出了 %@",mode);
CFRelease(mode);
break;
}
case kCFRunLoopAllActivities: {
CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
NSLog(@"kCFRunLoopAllActivities %@",mode);
CFRelease(mode);
break;
}
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), obRef, kCFRunLoopCommonModes);
CFRelease(obRef);
strongSelf.timer = [NSTimer timerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
// NSLog(@"timerCount = %d",self->_timerCount++);
NSLog(@"timerCount = %d",timerCount++);
}];
//NSDefaultRunLoopMode UITrackingRunLoopMode NSRunLoopCommonModes
[[NSRunLoop currentRunLoop] addTimer:strongSelf.timer forMode:NSRunLoopCommonModes];
[[NSRunLoop currentRunLoop] run];
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.timer invalidate];
});
}
-(void)dealloc{
NSLog(@"%s",__func__);
}
@end
運行後輸出:
2019-09-25 10:24:05.161121+0800 testRunLoop[19144:159757] kCFRunLoopEntry 進入 kCFRunLoopDefaultMode
2019-09-25 10:24:06.162717+0800 testRunLoop[19144:159757] timerCount = 0
//...省略
2019-09-25 10:24:14.162738+0800 testRunLoop[19144:159757] timerCount = 8
2019-09-25 10:24:15.161226+0800 testRunLoop[19144:159625] -[TestTimer dealloc]
從上面的運行結果發現,runloop在定時器結束後並沒有退出循環,如果想在結束後結束子線程循環則需要調用:[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
2019-09-25 10:28:39.161729+0800 testRunLoop[19809:166720] kCFRunLoopEntry 進入 kCFRunLoopDefaultMode
2019-09-25 10:28:40.165492+0800 testRunLoop[19809:166720] timerCount = 0
//...省略
2019-09-25 10:28:49.162015+0800 testRunLoop[19809:166720] timerCount = 9
2019-09-25 10:28:49.162109+0800 testRunLoop[19809:166667] -[TestTimer dealloc]
2019-09-25 10:28:49.162376+0800 testRunLoop[19809:166720] kCFRunLoopExit 退出了 kCFRunLoopDefaultMode
4. 線程保活
注意:NSRunloop的run方法是無法停止的,一旦掉用該方法就等於開啓一個永遠不會銷燬的線程;及即使調用
CFRunloopStop()
也只會是停止一次
的run;及調用run會無限
開啓run方法;
看一下實現:
#import "LYMPermemantThread.h"
@interface LYMThread : NSThread
@end
@implementation LYMThread
-(void)dealloc{
NSLog(@"%s",__func__);
}
@end
@interface LYMPermemantThread ()
@property(nonatomic,strong)LYMThread *innerThred;
@property(nonatomic,assign,getter=isStopped)BOOL stopped;
//@property(nonatomic,copy)void(^innerHandler)(void);
@end
@implementation LYMPermemantThread
- (instancetype)init{
if (self = [super init]) {
self.stopped = NO;
__weak typeof(self) weakSelf = self;
self.innerThred = [[LYMThread alloc]initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
while (weakSelf && !weakSelf.isStopped) {//一次循環後會退出,這裏爲防止退出,加入循環在需要退出的時候退出線程
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}];
[self.innerThred start];
}
return self;
}
- (void)run{
// if (!_innerThred) {
// return;
// }
// [self.innerThred start];
}
- (void)execTaskWithHandler:(void(^)(void))handler{
if (!_innerThred || !handler) {
return;
}
[self performSelector:@selector(__execBcock:) onThread:self.innerThred withObject:handler waitUntilDone:NO];
}
- (void)stop{
if (!_innerThred) {
return;
}
[self performSelector:@selector(__stop) onThread:self.innerThred withObject:nil waitUntilDone:YES];
}
- (void) __stop{
self.stopped = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
self.innerThred = nil;
}
- (void)__execBcock:(void(^)(void))handler{
if (handler) {
handler();
}
}
-(void)dealloc{
NSLog(@"%s",__func__);
[self stop];
}
@end
使用CF框架實現:
- (instancetype)init{
if (self = [super init]) {
self.stopped = NO;
__weak typeof(self) weakSelf = self;
self.innerThred = [[LYMThread alloc]initWithBlock:^{
// [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
//
// while (weakSelf && !weakSelf.isStopped) {
// [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
// }
CFRunLoopSourceContext cotext = {0};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &cotext);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, false);//false 標識source結束後不退出runloop
}];
[self.innerThred start];
}
return self;
}