白话文-RunLoop

没有runloop 就意味着app一运行就会退出(换句话说,runloop保护着app不会被退出)

RunLoop和线程的关系

  • 和线程是1对1的(存在于一个全局的map中, 线程作为key, runLoop 作为value)
  • 线程中默认是没有runloop的(主线程在AppDelegate中会自动获取(创建) )
  • 通过[NSRunLoop currentRunLoop];创建runLoop ;
  • 通过[NSRunLoop mainRunLoop];获取主线程runLoop ;
  • Runloop会在线程结束的时候销毁

RunLoop创建源码分析

  • 注意阅读注释
[CFRunLoop currentRunLoop];

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

static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFLock_t loopsLock = CFLockInit;
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
    // t : 传入的当前线程
    if (pthread_equal(t, kNilPthreadT)) {
    t = pthread_main_thread_np();
    }
    __CFLock(&loopsLock);
    // __CFRunLoops 静态map 初始化为null
    if (!__CFRunLoops) {
    // 创建全局 runloopMap和主线程的runloop,
        __CFUnlock(&loopsLock);
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
    CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
    CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
    // 将 dict 赋值给__CFRunLoops (搞不懂__CFRunLoops的可以看这里)
    if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
        CFRelease(dict);
    }
    CFRelease(mainLoop);
        __CFLock(&loopsLock);
    }
    // 使用 入参的 t 在 全局的map中 获取到响应的runloop
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    __CFUnlock(&loopsLock);
    // 如果没有获取到runloop, 则新建一个runloop
    if (!loop) {
    // 新建一个
    CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        __CFLock(&loopsLock);
    // 再次获取当先线程runloop
    loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    if (!loop) {
        // 确定当前线程没有runloop 将上面新创建的newloop保存到全局的map中
        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的状态

// 通过block的方式回调
 CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"######进入kCFRunLoopEntry");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"######即将处理Timer事件");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"######即将处理Source事件");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"######即将休眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"######被唤醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"######退出RunLoop");
                break;
            default:
                break;
        }
        CFRunLoopMode mode = CFRunLoopCopyCurrentMode(CFRunLoopGetCurrent());
        NSLog(@"当前runloop模式 - %@", mode);
        CFRelease(mode);
    });
    CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);
    CFRelease(observer);

RunLoop的应用

定时器失效

Q: 定时器失效问题
A: 因为timer模式是工作在runloop的kCFRunLoopDefaultMode模式下,
当滑动scorllView时候runloop会切换到UITrackingRunLoopMode模式下,所以导致timer定时器失效;
解决方法

// 可以将timer标记为CommonModes
[NSRunLoop currentRunLoop]addTimer:timer forMode:kCFRunLoopCommonModes];

线程保活

需要保护线程不退出可以给子线程添加runloop来保证线程不被退出
如果需要在适当的时候销毁线程 runloop 一定不能通过 [runloop run] 方法开启
PS: runloop提供的run方法是无法销毁的,其专门用于开启一个永不销毁的线程
如果需要在适当的时候销毁子线程, 就需要自己实现runloop的开启方法

// 这一句 是在dealloc中执行stop函数时防止对象已经被释放导致weakSelf为nil, 判断失效导致runloop无法销毁
// while(weakSelf && !wealSelf.isStop) 
while(!isStop) {
  [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
// 当需要结束线程的时候 在相关的方法中 将 stop 标记为no 用以结束runloop 从而退出线程
-(void)stop{
     // 注意! waitUntilDone 参数需要设置为YES, 不然如果在dealloc中执行stop函数 waitUntilDone设置为NO的话会进行异步操作导致野指针
    [self performSelector:@selector(stopThread) onThread:self.thread withObject:nil waitUntilDone:YES];
}
-(void)stopThread{
    isStop = false;
    CFRunLoopStop(CFRunLoopGetCurrent())
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,728评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,220评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,936评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,976评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,981评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,468评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,843评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,817评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,353评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,384评论 3 346
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,510评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,113评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,833评论 3 338
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,290评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,419评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,055评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,577评论 2 365

推荐阅读更多精彩内容