iOS RunLoop基础知识

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
i OS 中有 2 API 来访问和使用 RunLoop
Foundation NSRunLoop
Core Foundation CFRunLoopRef

NSRunLoop是对C语言 CFRunLoopRef 的封装,CFRunLoopRef是开源的

RunLoop与线程

  • 每条线程都有唯一的一个与之对应的RunLoop对象
  • RunLoop保存在一个全局的Dictionary里,线程作为keyRunLoop作为value
  • 线程刚创建时并没有RunLoop对象,RunLoop会在第一次获取它时创建(主线程也是)
CFRunLoopGetCurrent();
[NSRunLoop currentRunLoop];
  • RunLoop会在线程结束时销毁
  • 主线程的RunLoop已经自动获取(创建),子线程默认没有开启RunLoop

RunLoop是存在于堆上的

[NSRunLoop currentRunLoop]; // 获得当前线程的RunLoop对象

[NSRunLoop mainRunLoop]; // 获得主线程的RunLoop对象

Core Foundation

CFRunLoopGetCurrent(); // 获得当前线程的RunLoop对象

CFRunLoopGetMain(); // 获得主线程的RunLoop对象

Core Foundation中关于RunLoop5个类

CFRunLoopRef

CFRunLoopModeRef

CFRunLoopSourceRef

CFRunLoopTimerRef

CFRunLoopObserverRef

CFRunLoopModeRef 代表 RunLoop 的运行模式
一个 RunLoop 包含若干个 Mode ,每个 Mode 又包含若干个 Source0/Source1/Timer/Observer
RunLoop 启动时只能选择其中一个 Mode ,作为 currentMode
如果需要切换 Mode ,只能退出当前 Loop ,再重新选择一个 Mode 进入
不同组的 Source0/Source1/Timer/Observer 能分隔开来,互不影响
如果 Mode 里没有任何 Source0/Source1/Timer/Observer RunLoop 会立马退出

 

timers:NStimer 

observers:监听器

sources0、source1:平时的点击事件、刷新事件,设置某个UIView的背景色等

常见的2Mode

  • 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资源效果

面试题:

Runloop的原理 - 简书

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中装着kCFRunLoopDefaultModeUITrackingRunLoopMode

_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行 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值