iOS底层-- RunLoop

手动目录

  • RunLoop 6大响应事件
  • RunLoop 与线程的关系
  • RunLoop状态监听
  • RunLoop 数据结构
  • RunLoop流程
    • 如何进行休眠的
  • RunLoop 与autoreleasePool
  • RunLoop 与GCD
  • RunLoop 应用
    • 线程保活

什么是RunLoop?
RunLoop 是循环运行,在程序运行过程中,循环做一些事情。
RunLoop 是通过内部的运行循环,来对消息/事件进行管理的一个对象。

其作用是
1、保持程序的持续运行
2、处理APP中的各种事件(触摸、定时器、performSelector)
3、节省cpu资源、提供程序的性能:该做事就做事,该休息就休息

RunLoop 6大响应事件

  • block: __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
  • 调用Timer:__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__
  • 响应source0:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
  • 响应source1:__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
  • GCD主队列__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__
  • observer源:比如通知__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__

当 RunLoop 进行回调时,一般都是通过一个很长的函数调用出去 (call out), 当你在你的代码中下断点调试时,通常能在调用栈上看到这些函数。下面是这几个函数的整理版本,如果你在调用栈中看到这些长函数名,在这里查找一下就能定位到具体的调用地点了:

可结合地步的【流程图】来看

{
    /// 1. 通知Observers,即将进入RunLoop
    /// 此处有Observer会创建AutoreleasePool: _objc_autoreleasePoolPush();
    __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);
    do {
 
        /// 2. 通知 Observers: 即将触发 Timer 回调。
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers);
        /// 3. 通知 Observers: 即将触发 Source (非基于port的,Source0) 回调。
        __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources);
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
 
        /// 4. 触发 Source0 (非基于port的) 回调。
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
 
        /// 6. 通知Observers,即将进入休眠
        /// 此处有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唤醒的,回调Timer
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer);
 
        /// 9. 如果是被dispatch唤醒的,执行所有调用 dispatch_async 等方法放入main queue 的 block
        __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block);
 
        /// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件唤醒了,处理这个事件
        __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 与线程的关系

一一对应的关系。

  • 在内部,线程-runloop 在内部以key-value的形式存储在一个全局字典中 。
  • 当然,子线程中中,如果不主动去获取,runloop是没有的。主线程中的runloop是在main函数的 UIApplicationMain中创建的。

NSRunLoop 是对 CFRunLoopRef 的包装,
在代码中,一般有这几种写法

    NSRunLoop *mainLoop1    = [NSRunLoop mainRunLoop];
    NSRunLoop *currentLoop1 = [NSRunLoop currentRunLoop];
    CFRunLoopRef mainLoop2  = CFRunLoopGetMain();
    CFRunLoopRef runloop2 = CFRunLoopGetCurrent();

底层都是C语言代码,在追踪源码的时候,要用CFRunLoopGetMain 或者CFRunLoopGetCurrent来追踪

我们在源码中能找到这样的代码:(源码不是工程,创建一个空工程,将源码拖进去)

// 追踪 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());
        
    // 进行绑定 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存在一个字典CFMutableDictionaryRef,
字典中 以key-value 的形式存取,CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);

类似于这样
NSMutableDictionary *runloops = [NSMutableDictionary new];
runloops[@"祝线程"] = mainRunloop;
runloops[@"线程1"] = runloop1;
runloops[@"线程2"] = runloop2;

RunLoop状态监听

RunLoop 有几种状态

enum CFRunLoopActivity {
    kCFRunLoopEntry              = (1 << 0),    // 即将进入Loop   
    kCFRunLoopBeforeTimers      = (1 << 1),    // 即将处理 Timer        
    kCFRunLoopBeforeSources     = (1 << 2),    // 即将处理 Source  
    kCFRunLoopBeforeWaiting     = (1 << 5),    // 即将进入休眠     
    kCFRunLoopAfterWaiting      = (1 << 6),    // 刚从休眠中唤醒   
    kCFRunLoopExit               = (1 << 7),    // 即将退出Loop  
    kCFRunLoopAllActivities     = 0x0FFFFFFFU  // 包含上面所有状态  
};

RunLoop的状态是可以添加监听来看看是怎样的状态

// 方法1:   指定 执行方法的 方式
void observeRunLoopActicities(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
    switch (activity) {
        case kCFRunLoopEntry:
            NSLog(@"kCFRunLoopEntry");
            break;
       ......
        default:
            break;
    }
}

{
//     创建Observer
    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, observeRunLoopActicities, NULL);
    // 添加Observer到RunLoop中
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
    // 释放
    CFRelease(observer);
}


// 方法2:  block 方式
{
    // 创建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 数据结构

追踪源码 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 关键5个信息

  • _pthread:
    与之对应的线程
  • _commonModes:
  • _commonModeItems:
    在commonModes状态下运行的对象(例如Timer)
  • _currentMode:
    在当前loop下运行的mode(即使有多种mode,但同一时刻只能在一个mode下运行)
  • modes:
    运行的所有模式----- 类似于CFMutableSetRef< CFRunLoopModeRef >

__CFRunLoopMode 关键的信息

  • _sources0
    触摸事件处理
    performSelector:onThread:withObject:waitUntilDone:
  • _sources1
    基于Port的线程间的通讯
    系统事件捕捉(比如点击事件,由source1捕捉,然后包装之后交给source0处理)
  • _observers
    监听RunLoop的状态
    UI刷新(在BeforeWaiting的时候刷新)
    AutoreleasePool
  • _timers
    NSTimer
    performSelector:withObject:、 afterDelay:

RunLoopMode 类型

  • NSDefaultRunLoopMode
    App的默认Mode
  • UITrackingRunLoopMode
    滚动的时候处于这个mode
  • NSRunLoopCommonModes
    这是一个占位用的Mode,不是一种真正的Mode,它是上面2个mode的集合
  • UIInitializationRunLoopMode
    在刚启动App时进入的第一个Mode,启动完成后就不再使用
  • GSEventReceiveRunLoopMode
    接受系统事件的Mode,通常由系统去使用

前三种mode 对外提供,后面的2中不能使用

RunLoop 对应关系
RunLoop结构

为什么 一个runloop里包含多个mode?
不同mode中的 source0、source1、timer、observe 能分割开来,互不影响。

RunLoop流程

流程问题,从run开始
(内部代码太多,贴一份精简的代码)

// 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);
    
    /// 首先根据modeName找到对应mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    
    /// 通知 Observers: RunLoop 即将进入 loop。
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    /// 内部函数,进入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);
        }
        
        
        /// 判断有无端口消息(Source1)
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            /// 处理消息
            goto handle_msg;
        }
        
        /// 通知 Observers: 即将进入休眠
        __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: 被唤醒,结束休眠
        __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 进入loop(entry)

  • 2、通知observer 即将处理Timer(BeforeTimers)

  • 3、通知observer即将处理Sources (BeforeSources )

  • 4、处理GCD

  • 5、处理source0

    • 5.1 如果source0 有GCD需要处理,就处理GCD
  • 6、判断有没有端口消息(source1),有的话 继续处理

  • 7、通知 observe即将进入休眠(BeforeWaiting),进入休眠之后进行事件监听。(会一直卡在这直到被其他唤醒)

  • 8、通知observe结束休眠(AfterWaiting)

    • 8.1、Timer唤醒 : 处理Timer
    • 8.2、GCD唤醒:处理GCD
    • 8.3、source1唤醒:处理Source1
  • 9、执行GCD

  • 10、根据之前的执行结果 决定loop是继续循环 还是退出

  • 11、通知observe 退出(exit )。

流程图

这里有一个点需要注意 :
能够唤醒loop的有3个:TimerGCDSource1
Source0不能直接唤醒loop
source0是用户触摸事件,实际上是有系统(Source1)捕捉到点击事件,然后包装之后交给source0处理

如何进行休眠的

在用户层,是无法达到休眠(等待而不执行任何事物)的目的。通过内核去操作。

以下内容摘录于:深入理解RunLoop
mach_msg() 函数实际上是调用了一个 Mach 陷阱 (trap),即函数mach_msg_trap(),陷阱这个概念在 Mach 中等同于系统调用。当你在用户态调用 mach_msg_trap() 时会触发陷阱机制,切换到内核态;内核态中内核实现的 mach_msg() 函数会完成实际的工作,如下图:

休眠的实现

RunLoop 与autoreleasePool

  • 即将进入Loop(kCFRunLoopEntry),其回调内会调用 _objc_autoreleasePoolPush() 创建自动释放池。其order是-2147483647,优先级最高,保证创建释放池发生在其他所有回调之前。
  • 准备进入休眠(kCFRunLoopBeforeWaiting),此时调用 _objc_autoreleasePoolPop() 对释放池里面的 对象进行release操作
  • 即将退出Loop(kCFRunLoopExit)此时调用 _objc_autoreleasePoolPop()释放自动释放池。这个 Observer的order是2147483647,确保池子释放在所有回调之后。

RunLoop 与GCD

RunLoop与GCD的关系与2点:
1、在do..while 循环中,判断runloop有没有超时,用的是GCD的source_timer
2、当回主线程,也就是 GCD使用main_queue的时候,会向runloop发送一个_MAIN_DISPATCH_QUEUE的消息来唤醒runloop。这也是为什么子线程不能刷UI的原因之一。

  • 子线程 的GCD不能做UI的刷新等操作。是因为在子线程中,无法唤醒runloop进行UI刷新。
    我们做以下 操作:
    - (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];
    }
    
    执行这段代码,虽然设置backgroundColor的时候,颜色没有发生变化,但是当dispatch_after执行,或者主动执行runloop run 操作 的时候,颜色成功的改变了。

RunLoop 应用

线程保活

上面说了runloop的原理。
那么就知道为什么有时候NSTimer不起效了。

RunLoop还能干什么? --线程保活
一般来说,我们在子线程里面执行完任务,子线程就会退出,但是,有时候我们需要在子线程多次执行任务,就需要线程一直不退出来节省消耗。
我们可以这样做。
------向子线程中 添加一个port (相当于向runloop中 添加了一个 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];         // 这个需要注意,内部会无限循环run, 不能手动停止
            while (weakSelf && !weakSelf.stopped) {
                [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
            }
        NSLog(@"-------end-------");
    }];
    [self.thread start];
}

- (void)stop {
    if (!self.thread)  return;
    // 在子线程调用stop
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}

// 用于停止子线程的RunLoop
- (void)stopThread
{
    // 设置标记为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 *)touches withEvent:(UIEvent *)event {
    if (!self.thread)  return;
    [self performSelector:@selector(doSomeThings) onThread:self.thread withObject:nil waitUntilDone:NO];
}
- (void)doSomeThings {
    NSLog(@"1");
}

这里面有几个点需要注意:

  • 1、为什么没有用[runloop run] 而是用runMode:beforeDate?
    因为run 内部进行循环调用 runMode:beforeDate,我们需要自己打破这个循环,所以我们要进行自己的 while 条件循环。

  • 2、stop方法里面 的 waitUntilDone 必须为YES?
    考虑到可能会在dealloc里面调用stop,如果不等待,就会先释放了self,这个时候再进行 self.stopped 就是无效的。

参考 文章:
深入理解RunLoop

你可能感兴趣的:(iOS底层-- RunLoop)