iOS高级面试之NSRunLoop须知_crash之后让app回光返照 ios-程序员宅基地

技术标签: iOS高级面试  

参考:http://blog.csdn.net/agonie201218/article/details/51115677

 https://www.jianshu.com/p/edbe946c8a11

 https://segmentfault.com/a/1190000011151505

什么是RunLoop

从字面意思看,就是运行循环,兜圈圈儿。

  • 运行循环
  • 一个线程对应一个RunLoop,主线程的RunLoop默认已经启动,子线程的RunLoop得手动启动(调用run方法)
  • RunLoop只能选择一个Mode启动,如果当前Mode中没有任何Source(Sources0、Sources1)、Timer,那么就直接退出RunLoop。 
    这里写图片描述

基本作用

1、保持程序的持续运行,接受用户输入。 
2、处理APP中的各种事件(如触摸事件、定时器事件、Selector事件) 
3、节省CPU资源,提高程序性能:该做事时做事,该休息时休息。 
4、调用解耦。

RunLoop内部实现

其实它内部就是do-while循环,在这个循环内部不断地处理各种任务(比如Source、Timer、Observer)。

void message(int num) {
    printf("执行第%i个任务", num);
}

int main(int argc, const char * argv[]) {
    do {
        printf("有事吗? 没事我睡了");
        int number;
        scanf("%i", &number);
        message(number);
    } while (1);
    return 0;
}
   
   
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

iOS 中有2套API来访问和使用RunLoop

  • Foundation –> NSRunLoop
  • Core Foundation – >CFRunLoopRef

NSRunLoop 和 CFRunLoopRef都代表着RunLoop对象 
NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要研究CFRunLoopRef层面的API(Core Foundation层面)。

获得RunLoop对象

Foundation

[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
   
   
    
  • 1
  • 2

Core Foundation

CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
   
   
    
  • 1
  • 2

RunLoop与线程

每条线程都有唯一的一个与之对应的RunLoop对象。 主线程的RunLoop已经创建好了,子线程的RunLoop需要主动创建。 
RunLoop在第一次获取时创建,在线程结束时销毁。

RunLoop的结构

RunLoop的结构

Core Foundation 中关于RunLoop的5个类

CFRunLoopRef  
CFRunLoopModeRef 
CFRunLoopTimerRef 
CFRunLoopObserveRef 
CFRunLoopSource

  • CFRunLoopModeRef代表RunLoop的运行模式,系统默认注册了5个Mode 
    • NSDefaultRunLoopMode:App的默认Mode,通常主线程是在这个Mode下运行,RunLoop在同一时段只能且必须在一种特定Mode下Run,更换Mode时, 需要暂停当前的Loop,然后重启新的Loop。 
      • NSDefalutRunLoopMode      默认状态.空闲状态
      • UITrackingRunLoopMode     滑动ScrollView
      • UIInitializationRunLoopMode    私有,App启动时
      • NSRunLoopCommonModes     默认包括上面第一和第二
    • UITrackingRunLoopMode:界面跟踪Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
    • NSRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode。
  • CFRunLoopTimerRef是基于时间的触发器 
    • CFRunLoopTimerRef基本上说的就是NSTimer,它受RunLoop的Mode影响。
  • CFRunLoopSourceRef是事件源(输入源) 
    • Source0:处理App内部事件,App自己负责管理(触发),如UIEvent,CFSocket。
    • Source1:由RunLoop和内核管理,Mach port驱动,如CFMachPort、CFMessagePort。
  • CFRunLoopObserverRef是观察者,能够监听RunLoop的状态改变,向内部报告RunLoop当前状态的更改,框架中很多机制都由RunLoopObserve触发,如 CAAnimation。CFRunLoopObserverRef Activities

打印activity,查看RunLoop运行状态

// 1.创建Observer
// 第一个参数:用于分配该observer对象的内存
// 第二个参数:用以设置该observer所要关注的的事件
// 第三个参数:用于标识该observer是在第一次进入run loop时执行, 还是每次进入run loop处理时均执行
// 第四个参数:用于设置该observer的优先级
// 第五个参数: observer监听到事件时的回调block
    CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
        switch(activity)
        {
            case kCFRunLoopEntry:
                NSLog(@"即将进入loop");
                break;
            case kCFRunLoopBeforeTimers:
                NSLog(@"即将处理timers");
                break;
            case kCFRunLoopBeforeSources:
                NSLog(@"即将处理sources");
                break;
            case kCFRunLoopBeforeWaiting:
                NSLog(@"即将进入休眠");
                break;
            case kCFRunLoopAfterWaiting:
                NSLog(@"刚从休眠中唤醒");
                break;
            case kCFRunLoopExit:
                NSLog(@"即将退出loop");
                break;
            default:
                break;
        }
    });

//     2.添加监听
    /*
     第一个参数: 给哪个RunLoop添加监听
     第二个参数: 需要添加的Observer对象
     第三个参数: 在哪种模式下监听
     */
    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopDefaultMode);

//     3,释放observer
    CFRelease(observer);
   
   
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

RunLoop事件队列

每次运行run loop,你线程的run loop对会自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下:

RunLoop事件队列



RunLoopRunLoop的应用

NSTimer

设置RunLoopMode,让NSTimer不影响其他刷新,默认情况下NSTimer被加入NSDefalutRunLoopMode

如果不想让NSTimer受到组件或者动画影响,就添加到NSRunLoopCommonModes:

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(ddd) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
   
   
    
  • 1
  • 2

常驻线程

AFNetWorking 中创建RunLoop,创建一个常驻服务线程的很好的方法

  • 必须调用run才会执行死循环
  • NSRunLoop的model中必须有source/timer,死循环才不会退出
[[NSThread currentThread] setName:@"AFNetworking"];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefalutRunLoopMode]//一直活着

[runLoop run];
   
   
    
  • 1
  • 2
  • 3
  • 4
  • 5

自动释放池

activities = 0x1 = 1
1: 即将进入RunLoop : 创建一个自动释放池
activities = 0xa0 = 160 = 128 + 32
32:即将休眠 : 释放上一次的自动释放池, 创建一个新的自动释放池
128:即将退出RunLoop : 释放自动释放池
   
   
    
  • 1
  • 2
  • 3
  • 4
  • 5

让Crash的APP回光返照,接到Crash的Signal后手动重启RunLoop

CFRunLoopRef runloop = CFRunLoopGetCurrent();
NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runLoop));
while(1){
for (NSString *mode in allModes){
    CFRunLoopInMode((CFStringRef)mode,0.001,false);
}}
   
   
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

一个TableView延迟加载图片的新思路

[self.avatarImageView performSelector:@selector(serImage:) withObjetc:downloadedImage afterDelay:0 inModes:@[NSDefaultRunLoopMode]];

+ (NSThread *)networkRequestThread {
    static NSThread *networkRequestThread = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil];
        [networkRequestThread start];
    });

    return networkRequestThread;
}
   
   
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/shifang07/article/details/79126264

智能推荐

Sandboxie v5.45.2正式版 系统安全工具_sandboxie系统安全工具-程序员宅基地

文章浏览阅读141次。简介:菜鸟高手裸奔工具沙盘Sandboxie是一款国外著名的系统安全工具,它可以让选定程序在安全的隔离环境下运行,只要在此环境中运行的软件,浏览器或注册表信息等都可以完整的进行清空,不留一点痕迹。同时可以防御些带有木马或者病毒的恶意网站,对于经常测试软件或者不放心的软件,可放心在沙盘里面运行!下载地址:http://www.bytepan.com/J7BwpqQdKzR..._sandboxie系统安全工具

Mac技巧|如何在 MacBook上设置一位数登录密码-程序员宅基地

文章浏览阅读230次,点赞4次,收藏5次。Mac老用户都知道之前的老版本系统是可以设置一位数登陆密码的,但是更新到10.14以后就不可以了,今天就教大家怎么在新版本下设置Mac一位数登陆密码。

chatgpt中的强化学习 PPO_chatgpt使用的强化学习-程序员宅基地

文章浏览阅读3.4k次。本该到此结束,但是上述实现的时候其实是把生成的每一步的奖励都使用统一的句子级reward,但该代码其实也额外按照每个token来计算奖励值的,为了获取每个token的奖励,我们在生成模型的隐层表示上,多加一个线性层,映射到一维,作为每个状态的预测奖励值。类似的,在文本生成中我们也可以用蒙特卡洛方法来估计一个模型的状态价值。假如我们只采样到了s1和s2,没有采样到s3,由于7和3都是正向奖励,s1和s2的训练后生成的概率都会变大,且s1的概率变的更大,这看似合理,但是s3是未参与训练的,它的概率反而减小了。_chatgpt使用的强化学习

获取不规则多边形中心点_truf计算重心-程序员宅基地

文章浏览阅读433次,点赞10次,收藏8次。尝试了3种方法,都失败了!_truf计算重心

HDU 1950最长上升子序列 学习nlogn_poj 1631 hdu 1950为啥是最长上升子序列-程序员宅基地

文章浏览阅读406次。学习LIS_poj 1631 hdu 1950为啥是最长上升子序列

kubernetes===》二进制安装_sed -ie 's#image.*#image: ${ epic_image_fullname }-程序员宅基地

文章浏览阅读550次。一、节点规划主机名称IP域名解析k8s-m-01192.168.12.51m1k8s-m-02192.168.12.52m2k8s-m-03192.168.12.53m3k8s-n-01192.168.12.54n1k8s-n-02192.168.12.55n2k8s-m-vip192.168.12.56vip二、插件规划#1.master节点规划kube-apiserverkube-controller-manage_sed -ie 's#image.*#image: ${ epic_image_fullname }#g

随便推点

UAC绕过提权_uac白名单 提权-程序员宅基地

文章浏览阅读106次。UAC绕过提权_uac白名单 提权

Linux一键部署OpenVPN脚本-程序员宅基地

文章浏览阅读664次,点赞7次,收藏12次。每次架设OpenVPN Server就很痛苦,步骤太多,会出错的地方也多,基本很少一次性成功的。

头文件的相互包含问题_多个头文件相互包含-程序员宅基地

文章浏览阅读397次。 今天看了继承以及派生类,并且运行了教程中的一个实例,但是仍然有好多坑。主要如下:建立了一个基类bClass以及由基类bClass派生的一个dClass,并且建立两个头文件.h分别申明这两个类,在cpp程序中进行运行来检验。具体程序如下:#ifndef ITEM_BASE//为避免类重复定义,需要在头文件的开头和结尾加上如这个所示 #define ITEM_BASEclass bClass..._多个头文件相互包含

python -- PyQt5(designer)安装详细教程-程序员宅基地

文章浏览阅读1.3w次,点赞19次,收藏88次。PyQt5安装详细教程,安装步骤很详细

微信小程序scroll-view去除滚动条-程序员宅基地

文章浏览阅读154次。官方文档:https://developers.weixin.qq.com/miniprogram/dev/component/scroll-view.html。_scroll-view去除滚动条

POJ-3233 Matrix Power Series 矩阵A^1+A^2+A^3...求和转化-程序员宅基地

文章浏览阅读146次。S(k)=A^1+A^2...+A^k.保利求解就超时了,我们考虑一下当k为偶数的情况,A^1+A^2+A^3+A^4...+A^k,取其中前一半A^1+A^2...A^k/2,后一半提取公共矩阵A^k/2后可以发现也是前一半A^1+A^2...A^k/2。因此我们可以考虑只算其中一半,然后A^k/2用矩阵快速幂处理。对于k为奇数,只要转化为k-1+A^k即可。n为矩阵数量,m为矩阵..._a^1 a^2 ... a^k