标签:
既然从上一篇文章中已经知道了RunLoop是怎么运行的。那自己动手实现一个又何尝不可。这文章代码较多,仔细把代码看懂会有收获。在最后
也会有一些说明。
本文中所用到的demo为在我的gitHub上的SimpleRunLoop
首先RunLoop那一定要有事件输入源。创建一个定时输入源的类SimpleTimer:
#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
#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
#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类:
#import <Foundation/Foundation.h> @class SimpleTimer; @interface SimpleRunLoop : NSObject - (void)addTimer:(SimpleTimer *)timer; - (void)runUntilDate:(NSDate *)limitDate; @end
#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); //tow 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)类。
#import <Foundation/Foundation.h> #import "SimpleRunLoop.h" @interface NSThread (SimpleRunLoop) + (SimpleRunLoop *)currentSimpleRunLoop; @end
@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的原理,并不会投入实际使用。
2、SimpleRunLoop中的executeOnce函数中一定要把_timerQueue 拷贝到enumArry,因为在
遍历过程中是不能对数组的元数进行修改。这样如果在ViewController的timerFire中还可以继续新建
SimpleTimer事件源添加到队列。或者ViewController的timerFire中继续调用
[SimpleRunLoop runUntilDate]创建SimpleRunLoop的子循环。此时如果往SimpleRunLoop加上SimpleTimer事件源,
事件触发的调用就会在这个子循环里被调用。就是上一篇文章中NSRunLoop的行为一样。
3、系统的NSRunLoop实现肯定没有那么简单。那么我们这个SimpleRunLoop与NSRunLoop相差些什么呢,我觉有以下这些:
输入事件源SimpleRunLoop的输入源只能addTimer。而系统的有Port-Based Sources由内核自动发送,Custom Input Sources需要从其他线程手动发送。这个很关键,如果我们能够做到这两个其实也就可以做个真正的NSRunLoop。这可以继续深入研究。
探索 RunLoop (二)(RunLoop 自己动手实现RunLoop)
标签:
原文地址:http://www.cnblogs.com/chenxianming/p/5550669.html