美文网首页
Runloop入门篇

Runloop入门篇

作者: 向晨宇 | 来源:发表于2020-09-29 16:12 被阅读0次

    基础知识

    一. RunLoop的概念

    NSRunLoop事件驱动模型
    runloop可以想象成一个事件驱动的圆圈,我们在执行事件、手势、时间相应等等操作的时候,需要有监听者,这时候就有了源的概念,NSRunloop只有两种源,1.输入源 2.时间源
    ----伪代码如下:
    while (true) {
        [[NSRunLoop currentRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]];
    }
    

    注意:

    主线程会默认启动一个Runloop,而异步线程不会,while是为了防止RunLoop休眠,在没有“事件源”去驱动的时候,RunLoop会自动启动休眠模式

    二. 输入源

    包括三种:NSPort自定义源performSelector:OnThread:delay

    2.1 NSPort 基于端口的源

    Cocoa和 Core Foundation 为使用端口相关的对象和函数创建的基于端口的源提供了内在支持。Cocoa中你从不需要直接创建输入源。你只需要简单的创建端口对象,并使用NSPort的方法将端口对象加入到run loop。端口对象会处理创建以及配置输入源。

    NSPort一般分三种: NSMessagePort(基本废弃)NSMachPortNSSocketPort。 系统中的NSURLConnection就是基于NSSocketPort进行通信的,所以当在后台线程中使用NSURLConnection 时,需要手动启动RunLoop, 因为后台线程中的RunLoop默认是没有启动的.

    NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];//暂时不运行  
    [connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];//用NSRunLoopCommonModes  
    [connection start];
    

    可以用NSMachPort作为线程之间的通讯通道。例如在主线程创建子线程时传入一个NSPort对象,这样主线程就可以和这个子线程通讯啦,如果要实现双向通讯,那么子线程也需要回传给主线程一个NSPort

    2.2 自定义源

    在Core Foundation程序中,必须使用CFRunLoopSourceRef类型相关的函数来创建自定义输入源,接着使用回调函数来配置输入源。Core Fundation会在恰当的时候调用回调函数,处理输入事件以及清理源。常见的触摸、滚动事件等就是该类源,由系统内部实现。
    一般我们不会使用该种源,第三种情况已经满足我们的需求

    2.3 performSelector:OnThread

    Cocoa提供了可以在任一线程执行函数(perform selector)的输入源。和基于端口的源一样,perform selector请求会在目标线程上序列化,减缓许多在单个线程上容易引起的同步问题。而和基于端口的源不同的是,perform selector执行完后会自动清除出run loop
    此方法简单实用,使用也更广泛.

    performSelectorOnMainThread:withObject:waitUntilDone:  
    performSelectorOnMainThread:withObject:waitUntilDone:modes:
    
    performSelector:onThread:withObject:waitUntilDone:  
    performSelector:onThread:withObject:waitUntilDone:modes:
    
    performSelector:withObject:afterDelay:  
    performSelector:withObject:afterDelay:inModes:
    
    cancelPreviousPerformRequestsWithTarget:  
    cancelPreviousPerformRequestsWithTarget:selector:object:
    

    这些API最后两个是取消当前线程中调用,其他API是在主线程或者当前线程下的Run Loop中执行指定的@selector。

    三. 时间源

    需要注意的是scheduledTimerWith****开头生成的Timer会自动帮你以默认NSDefaultRunLoopMode模式加载到当前的Run Loop中,而其他接口生成的Timer则需要你手动使用-addTimer:forMode添加到Run Loop中。需要额外注意的是Timer的触发不会让Run Loop返回。

    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
    [[NSRunLoop currentRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]];
    

    四. RunLoop观察者

    我们可以通过创建CFRunLoopObserverRef对象来检测RunLoop的工作状态,它可以检测RunLoop的以下几种事件:
    Run loop入口
    Run loop将要开始定时
    Run loop将要处理输入源
    Run loop将要休眠
    Run loop被唤醒但又在执行唤醒事件前
    Run loop终止

    1. Run Loop Modes

    Mode Name Description
    Default NSDefaultRunLoopMode (Cocoa) kCFRunLoopDefaultMode (Core Foundation) 缺省情况下,将包含所有操作,并且大多数情况下都会使用此模式,默认的运行模式,除了NSConnection对象的事件
    Connection NSConnectionReplyMode (Cocoa) 此模式用于处理NSConnection的回调事件
    Modal NSModalPanelRunLoopMode (Cocoa) 模态模式,此模式下,RunLoop只对处理模态相关事件
    Common Modes NSRunLoopCommonModes (Cocoa) kCFRunLoopCommonModes (Core Foundation) 此模式用于配置”组模式”,一个输入源与此模式关联,则输入源与组中的所有模式相关联
    Event Tracking NSEventTrackingRunLoopMode (Cocoa) 此模式下用于处理窗口事件,鼠标事件等


    注意: NSRunLoopCommonModes是一种常用的模式集合,包含NSDefaultRunLoopModeNSTaskDeathCheckModeUITrackingRunLoopMode,这个模式存在的好处就是如果现在异步线程有个timer启动,不需要在所有的runloop mode中都去加一遍,只需要直接在NSRunLoopCommonModes加一次即可

    参考连接:

    1.iPhone开发之NSRunLoop的进一步理解
    2.iOS多线程编程Part 1/3 - NSThread & Run Loop
    3.苹果官方文档Run Loops
    4.Cocoa深入学习:NSOperationQueue、NSRunLoop和线程安全

    相关文章

      网友评论

          本文标题:Runloop入门篇

          本文链接:https://www.haomeiwen.com/subject/awpeuktx.html