美文网首页
iOS面试题之Runloop

iOS面试题之Runloop

作者: IOS应用 | 来源:发表于2022-04-24 16:24 被阅读0次
    Runloop
    • 为什么只有主线程的runloop是开启的
    • 为什么只在主线程刷新UI
    • PerformSelector和runloop的关系
    • 如何使线程保活

    Runloop

    作为一个合格的iOS开发者必须对runloop有一个更深入的了解,下面我们来回答一下 相关问题

    1.为什么只有主线程的runloop是开启的

    mian()函数中调用UIApplicationMain,这里会创建一个主线程,用于UI处理,为了让程序可以一直运行并接收事件,所以在主线程中开启一个runloop,让主线程常驻.

    2.为什么只在主线程刷新UI

    我们所有用到的UI都是来自于UIKit这个基础库.因为objc不是一门线程安全的语言所以存在多线程读写不同步的问题,如果使用加锁的方式操作系统开销很大,会耗费大量的系统资源(内存、时间片轮转、cpu处理速度…),加上上面讲到的系统事件的接收处理都在主线程,如果UI异步线程的话 还会存在 同步处理事件的问题,所以多点触摸手势等一些事件要保持和UI在同一个线程相对是最优解.

    另一方面是 屏幕的渲染是 60帧(60Hz/秒), 也就是1秒钟回调60次的频率,(iPad Pro 是120Hz/秒),我们的runloop 理想状态下也会按照时钟周期 回调60次(iPad Pro 120次), 这么高频率的调用是为了 屏幕图像显示能够垂直同步 不卡顿.在异步线程的话是很难保证这个处理过程的同步更新. 即便能保证的话 相对主线程而言 系统资源开销 线程调度等等将会占据大部分资源和在同一个线程只专门干一件事有点得不偿失.

    3.PerformSelector和runloop的关系

    当调用NSObect的 performSelector:相关的时候,内部会创建一个timer定时器添加到当前线程的runloop中,如果当前线程没有启动runloop,则该方法不会被调用.

    开发中遇到最多的问题就是这个performSelector: 导致对象的延迟释放,这里开发过程中注意一下,可以用单次的NSTimer替代.

    4.如何使线程保活?

    想要线程保活的话就开启该线程的runloop即可,注意:在NSThread执行的方法中添加while(true){},这样是模拟runloop的运行原理,结合GCD的信号量,在{}代码块中处理任务.

    但是注意 开启runloop的方法要正确

    如下代码

    //测试开启线程
    - (void)memoryTest {
        for (int i = 0; i < 100000; ++i) {
            NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
            [thread start];
            [self performSelector:@selector(stopThread) onThread:thread withObject:nil waitUntilDone:YES];
        }
    }
    //线程停止
    - (void)stopThread {
        CFRunLoopStop(CFRunLoopGetCurrent());
        NSThread *thread = [NSThread currentThread];
        [thread cancel];
    }
    //运行线程的runloop 注意 意添加的那个空port,否则会出现内存泄露
    - (void)run {
        @autoreleasepool {
            NSLog(@"current thread = %@", [NSThread currentThread]);
            NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
            if (!self.emptyPort) {
                self.emptyPort = [NSMachPort port];
            }
            [runLoop addPort:self.emptyPort forMode:NSDefaultRunLoopMode];
            [runLoop runMode:NSRunLoopCommonModes beforeDate:[NSDate distantFuture]];
        }
    }
    //下列代码用于模拟线程内部做的一些耗时任务
    - (void)printSomething {
        NSLog(@"current thread = %@", [NSThread currentThread]);
        [self performSelector:@selector(printSomething) withObject:nil afterDelay:1];
    }
    //模拟手动点击按钮 让 runloop停掉
    - (void)stopButtonDidClicked:(id)sender {
        [self performSelector:@selector(stopRunloop) onThread:self.thread withObject:nil waitUntilDone:YES];
    }
    
    - (void)stopRunloop {
        CFRunLoopStop(CFRunLoopGetCurrent());
    }
    复制代码
    

    以下文章可以做一个学习参考:

    GCD面试要点

    block面试要点

    Runtime面试要点

    RunLoop面试要点

    内存管理面试要点

    MVC、MVVM面试要点

    网络性能优化面试要点

    网络编程面试要点

    KVC&KVO面试要点

    数据存储面试要点

    混编技术面试要点

    设计模式面试要点

    UI面试要点

    相关文章

      网友评论

          本文标题:iOS面试题之Runloop

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