int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
UIApplicationMain函数作用
1、创建一个应用程序 创建应用程序代理AppDelegate
2、建立一个RunLoop,循环来捕捉处理用户的行为
什么是UIApplication?
1、一个iOS程序启动后创建的第一个对象就是UIApplication对象
2、每一个程序在运行期必须有且仅有一个UIApplication(或则其子类)的一个实例。创建UIApplication的单例实例,实现可以通过调用[UIApplication sharedApplication]
来得到这个单例实例的指针
3、利用UIApplication对象,能进行一些应用级别的操作
[[UIApplication sharedApplication]applicationState]
获取程序状态[[UIApplication sharedApplication]applicationIconBadgeNumber]
小红点个数
4、处理用户事件,它会起一个队列,把所有用户事件都放入队列,逐个处理,在处理的时候,它会发送当前事件到一个合适的处理事件的目标控件。
5、维护一个在本应用中打开的window列表(UIWindow实例,这样它就可以接触应用中的任何一个UIView对象。UIApplication实例会被赋予一个代理对象,以处理应用程序的生命周期事件(比如程序启动和关闭)、系统事件(比如来电、记事项警告)等等
RunLoop应用范畴:
- 定时器(Timer)、PerformSelector
- GCD Async Main Queue
- 事件响应、手势识别、界面刷新
- 网络请求
- AutoreleasePool
NSRunLoop是对C语言 CFRunLoopRef 的封装,CFRunLoopRef是开源的
RunLoop与线程
- 每条线程都有唯一的一个与之对应的RunLoop对象
- RunLoop保存在一个全局的Dictionary里,线程作为key,RunLoop作为value
- 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建(主线程也是)
CFRunLoopGetCurrent();
[NSRunLoop currentRunLoop];
- RunLoop会在线程结束时销毁
- 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop
RunLoop是存在于堆上的
[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象
[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象
Core Foundation
CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象
CFRunLoopGetMain(); // 获得主线程的RunLoop对象
Core Foundation中关于RunLoop的5个类
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
timers:NStimer
observers:监听器
sources0、source1:平时的点击事件、刷新事件,设置某个UIView的背景色等
常见的2种Mode
- kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
RunLoop作用:
程序并不会马上退出,而是保持运行状态
RunLoop的基本作用
保持程序的持续运行
处理App中的各种事件(比如触摸事件、定时器事件等)
节省CPU资源,提高程序性能:该做事时做事,该休息时休息
source1捕捉的事件,包装之后,由source0处理(performSelector:onThread:,内部基于Port的线程间通信)
self.view.backGroundColor = [UIColor redColor];
这行代码是:Observers监听器监听到runloop没有其他任务,即将进入进入休眠,在休眠之前,刷新UI
在休眠之前,会清理Autorelease Pool里面自动释放的对象
CFRunLoopObserverRef
RunLoop运行逻辑
多数GCD是不需要runloop的,像下面这个子线程回主线程刷新UI是需要runnloop的
Runloop休眠实现原理:Runloop如何做到真正的休眠?也有说法是线程阻塞
API调用分为:用户态、内核态
用户态的mach_msg 会调用内核态的mach_msg ,内核态mach_msg会根据情况决定是休眠还是唤起线程,然后再返回用户态mach_msg。达到节省CPU资源效果
面试题:
https://segmentfault.com/a/1190000023232656?utm_source=tag-newesthttps://segmentfault.com/a/1190000023232656?utm_source=tag-newest
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
//通知Observers:进入Loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
//具体要做的事情
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
//通知Observers:退出Loop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
//通知Observers:即将处理Timers
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
//通知Observers:即将处理Sources
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
//处理sorece0
if (__CFRunLoopDoSources0(rl, rlm, stopAfterHandle)) {
//处理完source0以后,如果返回的结果是YES,则处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
}
//判断有无source1
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
//如果有source1,就跳转到 handle_msg
goto handle_msg;
}
didDispatchPortLastTime = false;
//通知Observers 即将休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
//等待别的消息来唤醒当前线程
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
__CFRunLoopUnsetSleeping(rl);
//通知Observers:结束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:;
__CFRunLoopSetIgnoreWakeUps(rl);
if (被timer唤醒) {
//处理timer
!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())
}
else if (被GCD唤醒) {
//处理GCD相关事情
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else {//被source1唤醒
//处理source1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
}
//处理blocks
__CFRunLoopDoBlocks(rl, rlm);
//设置返回值
if (sourceHandledThisLoop && stopAfterHandle) {
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
retVal = kCFRunLoopRunFinished;
}
voucher_mach_msg_revert(voucherState);
os_release(voucherCopy);
} while (0 == retVal);
return retVal;
}
模式的理解:
_modes中装着kCFRunLoopDefaultMode和UITrackingRunLoopMode
_commonModes里面装着所有被标记为common的mode 上面两种mode正好被装在commonMode里
_commonModeItems里面放的是被标记为cpmmon的,比如NStimer
RunLoop在实际开中的应用
控制线程生命周期(线程保活,AFNetworking)
线程保活:如果需要频繁使用子线程做任务,为了避免线程频繁的销毁、创建
解决NSTimer在滑动时停止工作的问题
监控应用卡顿
性能优化
// NSRunLoop的run方法是无法停止的,它专门用于开启一个永不销毁的线程(NSRunLoop)
// [[NSRunLoop currentRunLoop] run];
/*
it runs the receiver in the NSDefaultRunLoopMode by repeatedly invoking runMode:beforeDate:.
In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers
*/
Runloop的退出:
线程销毁的时候退出,或者切换model的时候退出
线程执行完里面的任务就会结束
run 会让runloop会一直运行下去 的前提是先加一个事件进去
run 开启的线程保活是根本无法停止的
一直不会执行44行