RunLoop

2021-02-20  本文已影响0人  张_何

runloop 的状态待完善

相关面试题

什么是 RunLoop

RunLoop的作用

1、保持程序的持续运行
2、处理App中的各种事件(比如触摸事件、定时器事件等)
3、节省CPU资源,提高程序性能:该做事时做事,该休息时休息

int main (int argc, const char * argv[]){
  @autoreleasepool{
    NSLog(@"hello world");
  }
return 0;
}
int main(int argc, char *argv[]){
  @autoreleasepool{
    return UIApplicationMain(argc, argv,nil,NSStringFromClass([AppDelegate class]));
  }
}
int main(int argc, char *argc[]){
  int retVal = 0;
  do{
    // 睡眠中等待消息
    int message = sleep_and_wait();
    // 处理消息,处理完消息返回 0,继续 do...while 循环
    retVal = process_message(message);
  } while(0 == retVal);
  return 0;
}

RunLoop 对象

RunLoop 与线程

CFRunLoopRef CFRunLoopGetCurrent(void) {
    CHECK_FOR_FORK();
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    if (rl) return rl;
    return _CFRunLoopGet0(pthread_self());
}

上面我们看到其返回的是一个_CFRunLoopGet0函数

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());
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
// 这里我们可以看到 loop 对象是从CFDictionaryGetValue这个字典中取出来的,而且是以线程指针作为 key,runloop 对象作为 value 的,
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
// 这里如果从字典中去到的loop 对象是空,那么就会创建一个新的线程对象
    if (!loop) { 
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
// 这里将新的 newLoop 对象赋值给 loop,并保存在字典中
    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 相关的类

CFRunLoopRef :
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;
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;
};

其结构如下:


image.png
CFRunLoopModeRef
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
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 */
};
目前已知的Mode有5种
CFRunLoopSourceRef
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"touchesBegan");
}

点击屏幕触发断点,然后我们在控制台执行bt,得到函数调用栈信息如下:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x0000000105516efd RunLoop`-[ViewController touchesBegan:withEvent:](self=0x00007f8b9e705a60, _cmd="touchesBegan:withEvent:", touches=1 element, event=0x0000600001884d80) at ViewController.m:25:5
    frame #1: 0x00007fff48bf59c6 UIKitCore`forwardTouchMethod + 323
    frame #2: 0x00007fff48bf5872 UIKitCore`-[UIResponder touchesBegan:withEvent:] + 49
    frame #3: 0x00007fff48c0478f UIKitCore`-[UIWindow _sendTouchesForEvent:] + 622
    frame #4: 0x00007fff48c067f5 UIKitCore`-[UIWindow sendEvent:] + 4501
    frame #5: 0x00007fff48be0c39 UIKitCore`-[UIApplication sendEvent:] + 356
    frame #6: 0x00007fff48c6b096 UIKitCore`__dispatchPreprocessedEventFromEventQueue + 7328
    frame #7: 0x00007fff48c6e262 UIKitCore`__handleEventQueueInternal + 6565
    frame #8: 0x00007fff48c64dcb UIKitCore`__handleHIDEventFetcherDrain + 88
    frame #9: 0x00007fff23d9deb1 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #10: 0x00007fff23d9dddc CoreFoundation`__CFRunLoopDoSource0 + 76
    frame #11: 0x00007fff23d9d5b4 CoreFoundation`__CFRunLoopDoSources0 + 180
    frame #12: 0x00007fff23d981ae CoreFoundation`__CFRunLoopRun + 974
    frame #13: 0x00007fff23d97ac4 CoreFoundation`CFRunLoopRunSpecific + 404
    frame #14: 0x00007fff38b2fc1a GraphicsServices`GSEventRunModal + 139
    frame #15: 0x00007fff48bc7f80 UIKitCore`UIApplicationMain + 1605
    frame #16: 0x00000001055171c2 RunLoop`main(argc=1, argv=0x00007ffeea6e7cb8) at main.m:18:12
    frame #17: 0x00007fff519521fd libdyld.dylib`start + 1
    frame #18: 0x00007fff519521fd libdyld.dylib`start + 1
(lldb) 

在frame #9中我们看到__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ 可以验证该事件源来自source0。

下面我们验证performSelector:onThread:类型的,在viewDidLoad方法中加入如下代码,并在test方法内打上断点

    dispatch_async(dispatch_get_global_queue(0,0), ^{
         [self performSelectorOnMainThread:@selector(test) withObject:nil waitUntilDone:true];
    });
    
-(void)test{
    NSLog(@"test");
}

触发断点,在控制台执行bt,得到如下函数堆栈信息:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x0000000102a14e27 RunLoop`-[ViewController test](self=0x00007fcaab50b2e0, _cmd="test") at ViewController.m:28:5
    frame #1: 0x00007fff2593af42 Foundation`__NSThreadPerformPerform + 209
    frame #2: 0x00007fff23d9deb1 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
    frame #3: 0x00007fff23d9dddc CoreFoundation`__CFRunLoopDoSource0 + 76
    frame #4: 0x00007fff23d9d60c CoreFoundation`__CFRunLoopDoSources0 + 268
    frame #5: 0x00007fff23d981ae CoreFoundation`__CFRunLoopRun + 974
    frame #6: 0x00007fff23d97ac4 CoreFoundation`CFRunLoopRunSpecific + 404
    frame #7: 0x00007fff38b2fc1a GraphicsServices`GSEventRunModal + 139
    frame #8: 0x00007fff48bc7f80 UIKitCore`UIApplicationMain + 1605
    frame #9: 0x0000000102a15152 RunLoop`main(argc=1, argv=0x00007ffeed1e9cb8) at main.m:18:12
    frame #10: 0x00007fff519521fd libdyld.dylib`start + 1
    frame #11: 0x00007fff519521fd libdyld.dylib`start + 1
(lldb) 

从frame #2中可以看到__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__验证是source0。

CFRunLoopTimerRef
[NSTimer scheduledTimerWithTimeInterval:3 repeats:true block:^(NSTimer * _Nonnull timer) {
        [self test];
    }];

触发断点,在终端中执行bt,得到如下信息:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1
  * frame #0: 0x0000000102b44e27 RunLoop`-[ViewController test](self=0x00007fcc0bc0a490, _cmd="test") at ViewController.m:32:5
    frame #1: 0x0000000102b44d7b RunLoop`__29-[ViewController viewDidLoad]_block_invoke(.block_descriptor=0x00006000018c02d0, timer=0x00006000023a8900) at ViewController.m:25:9
    frame #2: 0x00007fff2594462e Foundation`__NSFireTimer + 72
    frame #3: 0x00007fff23d9e634 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
    frame #4: 0x00007fff23d9e2ce CoreFoundation`__CFRunLoopDoTimer + 1038
    frame #5: 0x00007fff23d9d92a CoreFoundation`__CFRunLoopDoTimers + 282
    frame #6: 0x00007fff23d9857e CoreFoundation`__CFRunLoopRun + 1950
    frame #7: 0x00007fff23d97ac4 CoreFoundation`CFRunLoopRunSpecific + 404
    frame #8: 0x00007fff38b2fc1a GraphicsServices`GSEventRunModal + 139
    frame #9: 0x00007fff48bc7f80 UIKitCore`UIApplicationMain + 1605
    frame #10: 0x0000000102b45152 RunLoop`main(argc=1, argv=0x00007ffeed0b9cb8) at main.m:18:12
    frame #11: 0x00007fff519521fd libdyld.dylib`start + 1
    frame #12: 0x00007fff519521fd libdyld.dylib`start + 1
(lldb) 

在frame #3:行中我们看到__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__验证是处理定时器事件的。

CFRunLoopObserverRef

RunLoop是怎样的一个循环

01、通知Observers:进入Loop
02、通知Observers:即将处理Timers
03、通知Observers:即将处理Sources
04、处理Blocks
05、处理Source0(可能会再次处理Blocks)
06、如果存在Source1,就跳转到第8步
07、通知Observers:开始休眠(等待消息唤醒)
08、通知Observers:结束休眠(被某个消息唤醒)
01> 处理Timer
02> 处理GCD Async To Main Queue
03> 处理Source1
09、处理Blocks
10、根据前面的执行结果,决定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop
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这个函数,源码如下:

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {     /* DOES CALLOUT */
    CHECK_FOR_FORK();
    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
    __CFRunLoopLock(rl);
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
    Boolean did = false;
    if (currentMode) __CFRunLoopModeUnlock(currentMode);
    __CFRunLoopUnlock(rl);
    return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    CFRunLoopModeRef previousMode = rl->_currentMode;
    rl->_currentMode = currentMode;
    int32_t result = kCFRunLoopRunFinished;
// 通知Observers:进入runloop
    if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 进行runloop 的核心逻辑
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知Observers:退出runloop
    if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

        __CFRunLoopModeUnlock(currentMode);
        __CFRunLoopPopPerRunData(rl, previousPerRun);
    rl->_currentMode = previousMode;
    __CFRunLoopUnlock(rl);
    return result;
}

通过源码看到返回的result是通过__CFRunLoopRun函数获取的,__CFRunLoopRun函数源码比较多,下面是提取出来的核心代码

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    uint64_t startTSR = mach_absolute_time();
// 先判断是否是stopped状态,如果是返回stopped的状态
    if (__CFRunLoopIsStopped(rl)) {
        __CFRunLoopUnsetStopped(rl);
        return kCFRunLoopRunStopped;
    } else if (rlm->_stopped) {
        rlm->_stopped = false;
        return kCFRunLoopRunStopped;
    }

    int32_t retVal = 0;
    do {
        // 通知Observers 即将处理Timers
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        // 通知Observers 即将处理Sources
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
        // 处理Blocks,是NSRunLoop 中的 performBlock:函数中的block
        __CFRunLoopDoBlocks(rl, rlm);

        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {、
            // 处理Blocks,是NSRunLoop 中的 performBlock:函数中的block
            __CFRunLoopDoBlocks(rl, rlm);
        }

        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);

        //如果有Sources1,就跳转打handle_msg标记处
        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
            msg = (mach_msg_header_t *)msg_buffer;
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                goto handle_msg;
            }
        }

        // 通知Observers即将休眠
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        // 进入休眠,等待其他消息唤醒
        __CFRunLoopSetSleeping(rl);
    // do not do any user callouts after this point (after notifying of sleeping)

        // Must push the local-to-this-activation ports in on every loop
        // iteration, as this mode could be run re-entrantly and we don't
        // want these ports to get serviced.

        __CFPortSetInsert(dispatchPort, waitSet);
        // 休眠后进去do...while(1)的死循环,循环里等待唤醒的消息,如果被唤醒执行break跳出死循环
        do {
            if (kCFUseCollectableAllocator) {
                // objc_clear_stack(0);
                // <rdar://problem/16393959>
                memset(msg_buffer, 0, sizeof(msg_buffer));
            }
            msg = (mach_msg_header_t *)msg_buffer;
            
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
            
            if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
                // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
                while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
                if (rlm->_timerFired) {
                    // Leave livePort as the queue port, and service timers below
                    rlm->_timerFired = false;
                    break;
                } else {
                    if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
                }
            } else {
                break;
            }
        } while (1);
        // 醒来
        __CFPortSetRemove(dispatchPort, waitSet);
        __CFRunLoopSetIgnoreWakeUps(rl);
        __CFRunLoopUnsetSleeping(rl);
        // 通知Observers 已经被唤醒
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

        handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);

        if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) { // 被timer唤醒,处理Timer
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        else if (livePort == dispatchPort) { // 被GCD 唤醒,处理GCD
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);

        } else { // 被sources1唤醒,处理Sources1
            if (rls) {
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
        }
        // 执行Blocks
        __CFRunLoopDoBlocks(rl, rlm);
        
        // 根据之前的执行结果,来决定怎么做,如果retVal被标记为非零,当前循环结束,返回retVal,CFRunLoopRunSpecific中通知observers退出runloop,如果是Stopped或Finished则外层CFRunLoopRun中的do...while循环结束,代码执行完毕,线程结束
        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);

    if (timeout_timer) {
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    } else {
        free(timeout_context);
    }

    return retVal;
}

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"111-----%@",[NSThread currentThread]);
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"222-----%@",[NSThread currentThread]);
        });
    });

在NSLog(@"222-----%@",[NSThread currentThread]);处断点,出发断点后,终端执行bt获得函数调用栈如下

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x000000010f21dd70 RunLoop`__29-[ViewController viewDidLoad]_block_invoke_2(.block_descriptor=0x000000010f2200a0) at ViewController.m:31:33
    frame #1: 0x000000010f487f11 libdispatch.dylib`_dispatch_call_block_and_release + 12
    frame #2: 0x000000010f488e8e libdispatch.dylib`_dispatch_client_callout + 8
    frame #3: 0x000000010f496d97 libdispatch.dylib`_dispatch_main_queue_callback_4CF + 1149
    frame #4: 0x00007fff23d9da89 CoreFoundation`__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    frame #5: 0x00007fff23d985d9 CoreFoundation`__CFRunLoopRun + 2041
    frame #6: 0x00007fff23d97ac4 CoreFoundation`CFRunLoopRunSpecific + 404
    frame #7: 0x00007fff38b2fc1a GraphicsServices`GSEventRunModal + 139
    frame #8: 0x00007fff48bc7f80 UIKitCore`UIApplicationMain + 1605
    frame #9: 0x000000010f21e102 RunLoop`main(argc=1, argv=0x00007ffee09e1cb8) at main.m:18:12
    frame #10: 0x00007fff519521fd libdyld.dylib`start + 1
    frame #11: 0x00007fff519521fd libdyld.dylib`start + 1

通过函数调用栈看到其是通过CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE函数来处理的

runloop示意图.png
线程保活

线程一旦执行完线程内的任务就会死亡,如果不想让线程在执行完任务之后死亡,就需要给线程的 runloop 的 mode 中添加source0、source1、timer、observer如果 runloop 中有四个中的任意一种就不会退出,runloop 不退出线程内的任务就没执行完,就不会销毁,我们可以给 runloop 添加一种事件源,让其不退出

@interface ZWThread : NSThread

@end
@implementation ZWThread
- (void)dealloc {
    NSLog(@"%s", __func__);
}

@end
@interface ViewController ()
@property (strong, nonatomic) ZWThread *thread;
@property (assign, nonatomic, getter=isStoped) BOOL stopped;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    __weak typeof(self) weakSelf = self;
    self.stopped = NO;
    self.thread = [[ZWThread alloc] initWithBlock:^{
        NSLog(@"%@----begin----", [NSThread currentThread]);
        // 往RunLoop里面添加Source\Timer\Observer, 这样 runloop 不会立即退出
        [[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
        while (!weakSelf.isStoped) { 
          // 这里运行 runloop,其实是"阻塞"线程,当 runloop 接收到事件时去处理事件,处理完了之后就休眠,当收到下次事件时重新判断 while 的条件是否成立,如果成立,继续执行循环体,如果不成立跳出 while 循环
            [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
        }
        // 只有当 runloop stop 的时候才会走到这里打印,没有 stop 的话,就一直在 while 循环体内处理事件和休眠.
        NSLog(@"%@----end----", [NSThread currentThread]);
    }];
    [self.thread start];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//    if (!self.thread) return;
    [self performSelector:@selector(test) onThread:self.thread withObject:nil waitUntilDone:NO];
}

// 子线程需要执行的任务
- (void)test{
    NSLog(@"after %s %@", __func__, [NSThread currentThread]);
}

- (IBAction)stop {
    if (!self.thread) return;
    // 在子线程调用stop(waitUntilDone设置为YES,代表子线程的代码执行完毕后,这个方法才会往下走)
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}

// 用于停止子线程的RunLoop
- (void)stopThread{
    // 设置标记为YES
    self.stopped = YES;
    
    // 停止RunLoop
    CFRunLoopStop(CFRunLoopGetCurrent());
    NSLog(@"%s %@", __func__, [NSThread currentThread]);
    
    // 清空线程
    self.thread = nil;
}

- (void)dealloc{
    NSLog(@"%s", __func__);
    
    [self stop];
}
上一篇下一篇

猜你喜欢

热点阅读