zoukankan      html  css  js  c++  java
  • 探索 NSRunLoop (二)(NSRunLoop 自己动手实现SimpleRunLoop)

            既然从上一篇文章中已经知道了RunLoop是怎么运行的。那自己动手实现一个又何尝不可。这文章代码较多,但希望看完这篇文章会对你有帮助。在最后

    也会有一些总结性的说明。

    本文中所用到的demo代码在我的gitHub上的SimpleRunLoop

       首先RunLoop那一定要有事件输入源。创建一个定时输入源的类SimpleTimer:

    SimpleTimer.h

    #import <Foundation/Foundation.h>
    #import <UIKit/UIKit.h>
    @interface SimpleTimer : NSObject
    + (SimpleTimer *)scheduledTimerWithTimerInterval:(CGFloat)interal target:(id)target selector:(SEL)selector repeat:(BOOL)repeat;
    @end
    SimpleTimerPrivate.h

    #import <Foundation/Foundation.h>
    #import "SimpleTimer.h"
    @interface SimpleTimer ()
    @property (nonatomic, strong) id target;
    @property (nonatomic, assign) SEL action;
    @property (nonatomic, assign) CFAbsoluteTime lasttime;
    @property (nonatomic, assign) CGFloat interval;
    @property (nonatomic, assign) BOOL isRepeat;
    - (void)invoke;
    @end
    
    
    SimpleTimer.m


    #import
    "SimpleTimer.h" #import "SimpleTimerPrivate.h" @implementation SimpleTimer + (SimpleTimer *)scheduledTimerWithTimerInterval:(CGFloat)interal target:(id)target selector:(SEL)selector repeat:(BOOL)repeat; { SimpleTimer *timer = [[SimpleTimer alloc] init]; timer.target = target; timer.action = selector; timer.interval = interal; timer.lasttime = CFAbsoluteTimeGetCurrent(); timer.isRepeat = repeat; return timer; } - (void)invoke { //remove the warning #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" if ([self.target respondsToSelector:self.action]) { [self.target performSelector:self.action withObject:nil]; } #pragma clang diagnostic pop } @end

    接下来就是收到事件时进行调用的RunLoop,SimpleRunLoop类:

    SimpleRunLoop.h

    #import <Foundation/Foundation.h>
    @class SimpleTimer;
    @interface SimpleRunLoop : NSObject
    
    - (void)addTimer:(SimpleTimer *)timer;
    - (void)runUntilDate:(NSDate *)limitDate;
    @end
    SimpleRunLoop.m

    #import "SimpleRunLoop.h"
    #import "SimpleTimerPrivate.h"
    
    @interface SimpleRunLoop ()
    {
        NSMutableArray<SimpleTimer *> *_timerQueue;
    }
    @end
    
    @implementation SimpleRunLoop
    
    - (id)init
    {
        self = [super init];
        
        if (self)
        {
            _timerQueue = [NSMutableArray<SimpleTimer *> array];
        }
        return self;
    }
    
    - (void)runUntilDate:(NSDate *)limitDate
    {
        BOOL finish = NO;
        while (!finish)
        {
            usleep(2 * 1000); //two second
            [self executeOnce];
            NSDate *date = [NSDate date];
            if ([date compare:limitDate] == NSOrderedDescending)
            {
                finish = YES;
            }
        }
    }
    
    - (void)addTimer:(SimpleTimer *)timer
    {
        [_timerQueue addObject:timer];
    }
    
    - (void)executeOnce
    {
        CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
        NSMutableArray<SimpleTimer *> *tempToDeleteArray = [NSMutableArray<SimpleTimer *> array];
        NSMutableArray<SimpleTimer *> *enumArray = [_timerQueue copy];
        for (SimpleTimer *timer in enumArray)
        {
            if (currentTime - timer.lasttime >= timer.interval)
            {
                if (timer.isRepeat)
                {
                    timer.lasttime = currentTime;
                }
                else
                {
                    [tempToDeleteArray addObject:timer];
                }
                [timer invoke];
            }
        }
        
        [_timerQueue removeObjectsInArray:tempToDeleteArray];
    }
    @end

    再想一想,NSRunLoop每个线程只会有一个,那么要实现这个,我就加了个NSThread(SimpleRunLoop)类。

    NSThread+SimpleRunLoop.h

    #import <Foundation/Foundation.h>
    #import "SimpleRunLoop.h"
    @interface NSThread (SimpleRunLoop)
    + (SimpleRunLoop *)currentSimpleRunLoop;
    @end
    NSThread+SimpleRunLoop.m

    @implementation NSThread (SimpleRunLoop)
    + (SimpleRunLoop *)currentSimpleRunLoop;
    {
        SimpleRunLoop *simpleRunLoop = nil;
        NSThread *currentThread = [NSThread currentThread];
        static const void *kSimpleHashKey = &kSimpleHashKey;
        simpleRunLoop = objc_getAssociatedObject(currentThread, kSimpleHashKey);
        
        if (!simpleRunLoop)
        {
            simpleRunLoop = [[SimpleRunLoop alloc] init];
            objc_setAssociatedObject(currentThread, kSimpleHashKey, simpleRunLoop, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        }
        
        return simpleRunLoop;
    }
    @end

    贴了这么多代码,总要讲怎么调用吧,以下就是使用的方式,想知道打印出什么把demo下下来运行一下就知道了。

    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        // Do any additional setup after loading the view, typically from a nib.
        
        NSLog(@"viewDidLoad begin");
        
        //create a input source
        SimpleTimer *timer = [SimpleTimer scheduledTimerWithTimerInterval:2 target:self selector:@selector(timerFire:) repeat:YES];
        
        //add input source to RunLoop
        SimpleRunLoop *simpleRunLoop = [NSThread currentSimpleRunLoop];
        [simpleRunLoop addTimer:timer];
        
        //begin the runloop
        [simpleRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]]; //run 10 second
        
        NSLog(@"viewDidLoad end");
    }
    
    - (void)timerFire:(NSTimer *)timer
    {
        NSLog(@"timerFire begin");
        NSLog(@"timerFire end");
    }

    这样就实现了我们自己的runloop了。

    说明:1、做这个SimpleRunLoop只是为了让大家更清晰的了解RunLoop的原理,虽然跟NSRunLoop相差很多,但万变不离其宗,原理是一样的。

       2、SimpleRunLoop中的executeOnce函数中一定要把_timerQueue 拷贝到enumArray,因为在

       遍历过程中是不能对数组的元数进行修改。这样就可以在ViewController的timerFire中继续新建

      SimpleTimer事件源添加到队列。或者ViewController的timerFire中继续调用

      [SimpleRunLoop runUntilDate]创建SimpleRunLoop的子循环,并往SimpleRunLoop加入SimpleTimer事件源,

      事件触发的调用就会在这个子循环里被调用。就是上一篇文章中NSRunLoop的行为一样。

      3、系统的NSRunLoop实现肯定没有那么简单。那么我们这个SimpleRunLoop与NSRunLoop相差些什么呢,我觉有以下这些:
      输入事件源SimpleRunLoop的输入源只能addTimer。而系统的NSRunLoop有Port-Based Sources由内核自动发送,Custom Input Sources需要从其他线程手动发送。这个很关键,如果我们能够做到把这两个事件源投进处理队列,那离NSRunLoop就更近一步了。还有RunLoopMode的切换等等。

    这些东西都是要跟底层东西打交道的。

    作者:xianmingchen
    出处:http://www.cnblogs.com/chenxianming/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任权利。
  • 相关阅读:
    软件系统架构演变
    PL/SQL语法
    ambari安装指南
    手把手带你使用JS-SDK自定义微信分享效果
    微信公众平台无法使用支付宝收付款的解决方案
    php创建含文件夹的zip文件
    捣鼓TinyMCE 粘贴图片并上传+Django后台
    捣鼓TinyMCE粘贴图片并上传+Flask后台
    关于Java 中Integer 和Long对象 对比的陷阱(简单却容易犯的错误)
    Java东西太多,记录一些知识点
  • 原文地址:https://www.cnblogs.com/chenxianming/p/5550669.html
Copyright © 2011-2022 走看看