zoukankan      html  css  js  c++  java
  • 深入理解RunLoop 整理资料

        http://blog.ibireme.com/2015/05/18/runloop/#base

     // RunLoop学习笔记,从CF层面了解由于CFRunLoopMode机制iOS程序ScrollView的滑动为何如此平滑的原因。还有介绍AFNetworking如何单独发起一个global thread内置runloop达到不占用主线程又不耗CPU资源的。

       // 简单的说run loop是事件驱动的一个大循环,如下代码所示

        

        int main(int argc, char * argv[]) {

            //程序一直运行状态

            while (AppIsRunning) {

                //睡眠状态,等待唤醒事件

                id whoWakesMe = SleepForWakingUp();

                //得到唤醒事件

                id event = GetEvent(whoWakesMe);

                //开始处理事件

                HandleEvent(event);

            }

            return 0;

        }

      //  Cocoa会涉及到Run Loops的

        

        //系统级:GCD,mach kernel,block,pthread

       // 应用层:NSTimer,UIEvent,Autorelease,NSObject(NSDelayedPerforming),NSObject(NSThreadPerformAddition),CADisplayLink,CATransition,CAAnimation,dispatch_get_main_queue()(GCD中dispatch到main queue的block会被dispatch到main RunLoop执行),NSPort,NSURLConnection,AFNetworking(这个第三方网络请求框架使用在开启新线程中添加自己的run loop监听事件)

       // 在Main thread堆栈中所处位置

        //堆栈最底层是start(dyld),往上依次是main,UIApplication(main.m) -> GSEventRunModal(Graphic Services) -> RunLoop(包含CFRunLoopRunSpecific,CFRunLoopRun,__CFRunLoopDoSouces0,__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION) -> Handle Touch Event

        

        //RunLoop原理

          //执行顺序的伪代码

        

        SetupThisRunLoopRunTimeoutTimer(); // by GCD timer

        do {

            __CFRunLoopDoObservers(kCFRunLoopBeforeTimers);

            __CFRunLoopDoObservers(kCFRunLoopBeforeSources);

            

            __CFRunLoopDoBlocks();

            __CFRunLoopDoSource0();

            

            CheckIfExistMessagesInMainDispatchQueue(); // GCD

            

            __CFRunLoopDoObservers(kCFRunLoopBeforeWaiting);

            var wakeUpPort = SleepAndWaitForWakingUpPorts();

            // mach_msg_trap

            // Zzz...

            // Received mach_msg, wake up

            __CFRunLoopDoObservers(kCFRunLoopAfterWaiting);

            // Handle msgs

            if (wakeUpPort == timerPort) {

                __CFRunLoopDoTimers();

            } else if (wakeUpPort == mainDispatchQueuePort) {

                // GCD

                __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__()

            } else {

                __CFRunLoopDoSource1();

            }

            __CFRunLoopDoBlocks();

        } while (!stop && !timeout);

      //  构成

        

        //Thread包含一个CFRunLoop,一个CFRunLoop包含一种CFRunLoopMode,mode包含CFRunLoopSource,CFRunLoopTimer和CFRunLoopObserver。

        

        CFRunLoopMode

        

       // RunLoop只能运行在一种mode下,如果要换mode当前的loop也需要停下重启成新的。利用这个机制,ScrollView过程中NSDefaultRunLoopMode的mode会切换UITrackingRunLoopMode来保证ScrollView的流畅滑动不受只能在NSDefaultRunLoopMode时处理的事件影响滑动。同时mode还是可定制的。

        

        NSDefaultRunLoopMode:        默认,空闲状态

        UITrackingRunLoopMode:       ScrollView滑动时

        UIInitializationRunLoopMode: 启动时

        NSRunLoopCommonModes:        Mode集合 Timer计时会被scrollView的滑动影响的问题可以通过将timer添加到NSRunLoopCommonModes来解决

        //将timer添加到NSDefaultRunLoopMode中

        [NSTimer scheduledTimerWithTimeInterval:1.0

                                         target:self

                                       selector:@selector(timerTick:)

                                       userInfo:nil

                                        repeats:YES];

        //然后再添加到NSRunLoopCommonModes里

        NSTimer *timer = [NSTimer timerWithTimeInterval:1.0

                                                 target:self

                                               selector:@selector(timerTick:)

                                               userInfo:nil

                                                repeats:YES];

        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

        

        

        CFRunLoopTimer

        

        //NSTimer是对RunLoopTimer的封装

        

        + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

        + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

        

        - (void)performSelector:(SEL)aSelector withObject:(id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray *)modes;

        

        + (CADisplayLink *)displayLinkWithTarget:(id)target selector:(SEL)sel;

        - (void)addToRunLoop:(NSRunLoop *)runloop forMode:(NSString *)mode;

        

        

        CFRunLoopSource

        

    //    source0:处理如UIEvent,CFSocket这样的事件

    //    source1:Mach port驱动,CFMachport,CFMessagePort

    //    CFRunLoopObserver

    //    

    //    Cocoa框架中很多机制比如CAAnimation等都是由RunLoopObserver触发的。observer到当前状态的变化进行通知。

        

        typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {

            kCFRunLoopEntry = (1UL << 0),

            kCFRunLoopBeforeTimers = (1UL << 1),

            kCFRunLoopBeforeSources = (1UL << 2),

            kCFRunLoopBeforeWaiting = (1UL << 5),

            kCFRunLoopAfterWaiting = (1UL << 6),

            kCFRunLoopExit = (1UL << 7),

            kCFRunLoopAllActivities = 0x0FFFFFFFU

        };

        //使用RunLoop的案例

        

        AFNetworking

         //使用NSOperation+NSURLConnection并发模型都会面临NSURLConnection下载完成前线程退出导致NSOperation对象接收不到回调的问题。AFNetWorking解决这个问题的方法是按照官方的guidhttps://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSURLConnection_Class/Reference/Reference.html#//apple_ref/occ/instm/NSURLConnection/initWithRequest:delegate:startImmediately:上写的NSURLConnection的delegate方法需要在connection发起的线程runloop中调用,于是AFNetWorking直接借鉴了Apple自己的一个Demohttps://developer.apple.com/LIBRARY/IOS/samplecode/MVCNetworking/Introduction/Intro.html的实现方法单独起一个global thread,内置一个runloop,所有的connection都由这个runloop发起,回调也是它接收,不占用主线程,也不耗CPU资源。

        

        + (void)networkRequestThreadEntryPoint:(id)__unused object {

            @autoreleasepool {

                [[NSThread currentThread] setName:@"AFNetworking"];

                

                NSRunLoop *runLoop = [NSRunLoop currentRunLoop];

                [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];

                [runLoop run];

            }

        }

        

        + (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;

        }

    //    类似的可以用这个方法创建一个常驻服务的线程。

    //    

    //    TableView中实现平滑滚动延迟加载图片

    //    

    //    利用CFRunLoopMode的特性,可以将图片的加载放到NSDefaultRunLoopMode的mode里,这样在滚动UITrackingRunLoopMode这个mode时不会被加载而影响到。

        

        UIImage *downloadedImage = ...;

        [self.avatarImageView performSelector:@selector(setImage:)

                                   withObject:downloadedImage

                                   afterDelay:0

                                      inModes:@[NSDefaultRunLoopMode]];

        //接到程序崩溃时的信号进行自主处理例如弹出提示等

        

        CFRunLoopRef runLoop = CFRunLoopGetCurrent();

        NSArray *allModes = CFBridgingRelease(CFRunLoopCopyAllModes(runLoop));

        while (1) {

            for (NSString *mode in allModes) {

                CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);

            }

        }

        

        异步测试

        

        - (BOOL)runUntilBlock:(BOOL(^)())block timeout:(NSTimeInterval)timeout

        {

            __block Boolean fulfilled = NO;

            void (^beforeWaiting) (CFRunLoopObserverRef observer, CFRunLoopActivity activity) =

            ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {

                fulfilled = block();

                if (fulfilled) {

                    CFRunLoopStop(CFRunLoopGetCurrent());

                }

            };

            

            CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(NULL, kCFRunLoopBeforeWaiting, true, 0, beforeWaiting);

            CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);

            

            // Run!

            CFRunLoopRunInMode(kCFRunLoopDefaultMode, timeout, false);

            

            CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopDefaultMode);

            CFRelease(observer);

            

            return fulfilled;

        }

    }

  • 相关阅读:
    bzoj5178 [Jsoi2011]棒棒糖 主席树+线段树二分
    bzoj4408 [Fjoi 2016]神秘数 & bzoj4299 Codechef FRBSUM 主席树+二分+贪心
    bzoj3123 [Sdoi2013]森林 树上主席树+启发式合并
    bzoj4448 [Scoi2015]情报传递 主席树+树上差分
    bzoj4399 魔法少女LJJ 线段树合并+线段树二分+并查集
    CF1009F Dominant Indices 长链剖分
    bzoj4543 [POI2014]Hotel加强版 长链剖分+树形DP
    bzoj4009 [HNOI2015]接水果 整体二分+扫描线+树状数组+dfs序
    bzoj4940 [Ynoi2016]这是我自己的发明 莫队+dfs序
    bzoj5016 & loj2254 [Snoi2017]一个简单的询问 莫队
  • 原文地址:https://www.cnblogs.com/LiuYisiTony/p/5131400.html
Copyright © 2011-2022 走看看