前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS开发-RunLoop

iOS开发-RunLoop

作者头像
孙寅
发布2020-06-02 22:44:40
8120
发布2020-06-02 22:44:40
举报
文章被收录于专栏:宜达数字

RunLoop

从字面意思来看:跑圈、运动循环 基本用法:保持程序持续运行、处理App中的各种事件(触摸事件、定时器事件、SEL等等) 为什么需要它:节省CPU资源、 提高性能

如果没有RunLoop,程序在执行到7行就结束了。

代码语言:javascript
复制
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

有Runloop后,程序就相当于一直在做循环

在这个循环内部不断地处理各种任务(比如Source、Timer、Observer)

代码语言:javascript
复制
int main(int argc, const char * argv[]) {
   
    BOOL running = YES;
    do{
        // 执行各种任务,处理各种时间
        
    }while (running);
    
    return 0;
}

程序中的Runloop----UIApplicationMain

代码语言:javascript
复制
int main(int argc, char *argv[]) 
{
       @autoreleasepool { 
              return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 
       }
 }

1.在UIApplicationMain函数内部就启动了一个RunLoop 2.UIApplicationMain函数一直没有返回,保持了程序的持续运行 3.这个默认启动的RunLoop是主线程关联的。 4.一个线程对应一个RunLoop,主线程的RunLoop默认已经启动 5.子线程的RunLoop得手动启动(调用run方法) 6.RunLoop只能选择一个Mode启动,如果当前Mode中没有任Source、Timer、Observer,那么就直接退出RunLoop

RunLoop里面有两套api用来访问和使用RunLoop 1、Foundation--NSRunLoop 2、Core Foundation --- CFRunloopRef 二者异同点: NSRunLoop和CFRunloopRef都代表RunLoop对象,NSRunLoop是对CFRunloopRef一层OC的封装

RunLoop与线程:

每条线程都有一个RunLoop对象,主线程默认已经创建好了,子线程需要主动创建 Runloop在第一次获取时创建,在线程结束后销毁。

代码语言:javascript
复制
   /* 1. Foundation  */
    // 获取当前线程
    NSRunLoop *roop = [NSRunLoop currentRunLoop];
    // 获取主线程
    [NSRunLoop mainRunLoop];

   /*2. Core Foundation  */
    // 获取当期线程
    CFRunLoopGetCurrent();
    // 获取主线程
    CFRunLoopGetMain();

RunLoop相关类(Runloop中如果没有Source,Observre,Timer,Mode,就会结束)

代码语言:javascript
复制
    // Runloop对象
    CFRunLoopRef;
 
    // Runloop事件源(数据源)
    CFRunLoopSourceRef;
    
    // Runloop观察者
    CFRunLoopObserverRef;
    
    // Runloop时间源
    CFRunLoopTimerRef;

    // Runloop模式
    CFRunloopModeRef;

Runloop里面相关类的互相关系-

Paste_Image.png

代码语言:javascript
复制
    // 获取当前Runloop的模式
    NSString *runloopMode = [NSRunLoop currentRunLoop].currentMode;

1.同一时间只可以运行其中的一种model 2.切换Model只能退出Runloop,重新进入 3.系统默认注册了5个Mode

代码语言:javascript
复制
//App默认的Mode,通常主线程是在这个Mode下运行
NSDefaultRunLoopMode:
//界面追踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动时不受其他Mode影响
UITrackingRunLoopMode
//这是一个占位用的Mode,不是一种正真的Mode
NSRunLoopCommonModes

//在刚启动App进入的第一个Mode,启动完成以后就不再使用
UIInitializationRunLoopMode
//接收系统事件的内部Mode,通常用不到
GSEventReceiveRunLoopMode

CFRunLoopTimerRef

CFRunLoopTimerRef是基于时间的触发器

CFRunLoopTimerRef基本上说就是NSTimer,它受RunLoop的Mode影响

代码语言:javascript
复制
NSTimer *time = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
// 定时器只运行在NSDefaultRunLoopMode下,一旦RunLoop进入其他模式,这个定时器就不会工作
[[NSRunLoop currentRunLoop] addTimer:time forMode:NSDefaultRunLoopMode];
    
// 定时器会跑在标记为common modes的模式下
// 标记为common modes的模式:包含:UITrackingRunLoopMode和kCFRunLoopDefaultMode
[[NSRunLoop currentRunLoop] addTimer:time forMode:NSRunLoopCommonModes];

GCD不受RunLoop的Mode影响

代码语言:javascript
复制
    // 获取一个全局并发队列
    /*
     #define DISPATCH_QUEUE_PRIORITY_HIGH 2
     #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0  默认
     #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
     #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 1. 设置定时器
    // dispatchQueue :决定了将来回调的方法在哪里执行。
    // dispatch_source_t timer  是一个OC对象
    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    
    // 2.指定定时期开始的时间和间隔的时间
    // DISPATCH_TIME_NOW :第2个参数:定时器开始时间   (也可以抽出来,设置间隔时间)
        dispatch_time_t startTime = dispatch_time(DISPATCH_TIME_NOW, 1.0 *NSEC_PER_SEC);
    
    // intervalInSeconds :第三个参数:定时器开始后的间隔时间
    // leewayInSeconds:第四个参数:间隔精准度,0代标最精准,传入一个大于0的数,代表多少秒的范围是可以接收的,主要为了提高程序性能,积攒一定的时间,Runloop执行完任务会睡觉,这个方法让他多睡一会,积攒时间,任务也就相应多了一点,而后一起执行
    // 开始时间

    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    
    // 3.指定定时器回调方法
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"___________");
    });
    // 开启定时器
    dispatch_resume(timer);

CFRunLoopSourceRef(事件源、输入源)

代码语言:javascript
复制
Port-Based Sources (端口)
Custom Input (自定义事件)
Cocoa Perform Selector Sources 

按照函数的调用栈 Source0:非基于Port的 Source1:基于Port 通过内核和其他线程通信,接收分发系统事件

CFRunLoopObserverRef(观察者)

能够监听RunLoop的状态改变

Paste_Image.png

CFRunLoopObserverRef

代码语言:javascript
复制
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),      // 1
    kCFRunLoopBeforeTimers = (1UL << 1), // 2
    kCFRunLoopBeforeSources = (1UL << 2), // 4
    kCFRunLoopBeforeWaiting = (1UL << 5), // 32
    kCFRunLoopAfterWaiting = (1UL << 6), // 64
    kCFRunLoopExit = (1UL << 7),       // 128
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

创建一个Observer

代码语言:javascript
复制
 /*
     第1个参数:如何给Observer分配存储空间
     第2个参数:需要监听的状态类型/kCFRunLoopAllActivities监听所有状态
     第3个参数:是否每次需要监听?
     第4个参数:优先级(0)
     第5个参数:监听状态改变后的回调
     */
    
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch (activity) {
            case kCFRunLoopEntry:
                NSLog(@"即将进入Runloop");
                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;
        }
    });
    /*
    第1个参数:要给那个RunLoop添加观察者
    第2个参数:添加的Observer对象
    第3个参数:在那种模式下监听
     */
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);

    //  释放对象
    CFRelease(observer);

由于ARC只管理Foundation框架的内容,所以我们在Core Foundation 框架创建的对象必须手动释放。 规律: 凡是带有copy、create、retain等字眼的函数,创建出来的CF对象,都需要在最后做一次release

官方对于RunLoop的解释:

Paste_Image.png

RunLoop处理逻辑,整理:自动释放池的生命周期

RunLoop在进入这个 kCFRunLoopBeforeWaiting时,会对自动释放池销毁

Paste_Image.png

Runloop:在开发中有什么作用?

1.NSTimer 2.ImageView的显示 3.PerformSelector 4.常驻线程 5.自动释放池

PerformSelector

代码语言:javascript
复制
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{  
  /*
     - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSString *> *)modes;
     */
    // 指定模式下进行特定的操作
    [self.imageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@"rightPic"] afterDelay:2.0 inModes:@[NSDefaultRunLoopMode]];
}

常驻线程: 默认情况下,一个线程只能使用一次,也就是只能执行一个操作 我们需要做得到就是强引用,保留线程,同时添加Source Or Timer,注:系统只会监测Source Or Timer,不会检查Observer

1.子线程的Runloop需要手动创建 2.子线程的Runloop需要手动开启 3.如果子线程的NSRunLoop没有设置Source Or Timer 那么子线程会立刻关闭。

代码语言:javascript
复制
- (void)ViewDidLoad
{
    [super viewDidLoad];
    SYThread *thread = [[SYThread alloc] initWithTarget:self selector:@selector(show) object:nil];
    self.thread = thread;
    [thread start];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self performSelector:@selector(test) onThread:self.thread withObject:@"时间计算" waitUntilDone:YES modes:@[NSRunLoopCommonModes]];
}

- (void)show
{
    NSLog(@"%s", __func__);
    // 这句话并没有意义,只是保证线程不死
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSRunLoopCommonModes];
    [[NSRunLoop currentRunLoop] run];
    // 上面线程不死,这句话永远不会打印
    NSLog(@"-------$$$$");
}
- (void)test
{
    NSLog(@"11111");
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • RunLoop
  • 如果没有RunLoop,程序在执行到7行就结束了。
  • 有Runloop后,程序就相当于一直在做循环
  • 程序中的Runloop----UIApplicationMain
  • RunLoop与线程:
  • RunLoop相关类(Runloop中如果没有Source,Observre,Timer,Mode,就会结束)
    • Runloop里面相关类的互相关系-
    • CFRunLoopTimerRef
    • GCD不受RunLoop的Mode影响
    • CFRunLoopSourceRef(事件源、输入源)
    • CFRunLoopObserverRef(观察者)
      • CFRunLoopObserverRef
        • 创建一个Observer
        • 官方对于RunLoop的解释:
        • RunLoop处理逻辑,整理:自动释放池的生命周期
        • Runloop:在开发中有什么作用?
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档