iOS高级技术:NSRunLoop须知(面试必备)
2017-11-10 20:52
567 查看
什么是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; }
iOS 中有2套API来访问和使用RunLoop
Foundation –> NSRunLoopCore Foundation – >CFRunLoopRef
NSRunLoop 和 CFRunLoopRef都代表着RunLoop对象
NSRunLoop是基于CFRunLoopRef的一层OC包装,所以要了解RunLoop内部结构,需要研究CFRunLoopRef层面的API(Core Foundation层面)。
获得RunLoop对象
Foundation[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象 [NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象 CFRunLoopGetMain(); // 获得主线程的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。
打印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);
RunLoop事件队列
每次运行run loop,你线程的run loop对会自动处理之前未处理的消息,并通知相关的观察者。具体的顺序如下: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];
常驻线程
AFNetWorking 中创建RunLoop,创建一个常驻服务线程的很好的方法必须调用run才会执行死循环
NSRunLoop的model中必须有source/timer,死循环才不会退出
[[NSThread currentThread] setName:@"AFNetworking"]; NSRunLoop *runloop = [NSRunLoop currentRunLoop]; [runLoop addPort:[NSMachPort port] forMode:NSDefalutRunLoopMode]//一直活着 [runLoop run];
自动释放池
activities = 0x1 = 1 1: 即将进入RunLoop : 创建一个自动释放池 activities = 0xa0 = 160 = 128 + 32 32:即将休眠 : 释放上一次的自动释放池, 创建一个新的自动释放池 128:即将退出RunLoop : 释放自动释放池
让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); }}
一个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; }
相关文章推荐
- iOS高级面试的11个技术问题
- iOS高级面试的11个技术问题
- 【转】Linux netstat命令详解,高级面试必备
- iOS--高级技术
- iOS开发——高级技术&本地化与国际化详解
- iOS开发——高级技术&通讯录功能的实现
- iOS面试必备看看总有好处
- iOS开发面试题目(高级向)
- JVM知识点总览-高级Java工程师面试必备
- JVM知识点总览-高级Java工程师面试必备
- iOS开发者面试必备:10个Objective-C基础面试题
- jvm知识点总览-高级Java工程师面试必备
- iOS 核心动画高级技术: iOS-Core-Animation-Advanced-Techniques
- 【转】iOS高级向的十道面试问题
- iOS高级调试&逆向技术-汇编寄存器调用
- iOS开发——高级技术&GameCenter服务
- iOS开发——高级技术&调用地图功能的实现
- 那些技术是中级Android工程师面试必备的
- jvm系列(八):jvm知识点总览-高级Java工程师面试必备
- JVM知识点总览-高级Java工程师面试必备