Now that you've learned from the previous article how Runloop works. It is also possible to do it yourself. This article code more, carefully read the code to understand there will be a harvest. At the end
There will be some clarification.
The demo used in this article is the Simplerunloop on my GitHub.
First Runloop that must have an event input source. Create a class for a timed input source 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"@ InterfaceID 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"@implementationSimpletimer+ (Simpletimer *) Scheduledtimerwithtimerinterval: (cgfloat) interal target: (IDtarget 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; returntimer;}- (void) invoke{//Remove the warning#pragmaClang Diagnostic push#pragmaClang diagnostic ignored "-warc-performselector-leaks"if([Self.target respondsToSelector:self.action]) {[Self.target performSelector:self.action withobject:nil]; }#pragmaClang Diagnostic Pop}@end
Next is the Runloop,simplerunloop class that is called when the event is received:
#import <Foundation/Foundation.h>@class simpletimer; @interface simplerunloop:nsobject-(void) AddTimer: (Simpletimer *) timer; -(void) Rununtildate: (NSDate *) limitdate; @end
#import "SimpleRunLoop.h"#import "SimpleTimerPrivate.h"@interfaceSimplerunloop () {Nsmutablearray<simpletimer *> *_timerqueue;}@end@implementationSimplerunloop- (ID) init{ Self=[Super Init]; if(self) {_timerqueue= [Nsmutablearray<simpletimer *>array]; } returnSelf ;}- (void) Rununtildate: (NSDate *) limitdate{BOOL finish=NO; while(!finish) {Usleep (2* +);//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 *timerinchEnumarray) { if(Currenttime-timer.lasttime >=timer.interval) {if(timer.isrepeat) {timer.lasttime=currenttime; } Else{[Temptodeletearray addobject:timer]; } [timer invoke]; }} [_timerqueue Removeobjectsinarray:temptodeletearray];}@end
Think again, nsrunloop each thread will only have one, then to achieve this, I added a nsthread (Simplerunloop) class.
#import <Foundation/Foundation.h>#import"SimpleRunLoop.h" @interface Nsthread (Simplerunloop)+ (Simplerunloop *) Currentsimplerunloop; @end
@implementationNsthread (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); } returnSimplerunloop;}@end
Paste so much code, always talk about how to call it, the following is the way to use, want to know what to print the demo down to run a bit to know.
@implementationViewcontroller- (void) viewdidload {[Super viewdidload]; //additional setup after loading the view, typically from a nib.NSLog (@"Viewdidload begin"); //Create a input sourceSimpletimer *timer = [Simpletimer scheduledtimerwithtimerinterval:2target:self selector: @selector (timerfire:) Repeat:yes]; //add input source to RunloopSimplerunloop *simplerunloop =[Nsthread Currentsimplerunloop]; [Simplerunloop Addtimer:timer]; //begin the Runloop[Simplerunloop rununtildate:[nsdate Datewithtimeintervalsincenow:Ten]];//Run Ten secondNSLog (@"Viewdidload End");}- (void) Timerfire: (Nstimer *) timer{NSLog (@"Timerfire begin"); NSLog (@"Timerfire End");}
So that we can achieve our own runloop.
Description: 1, do this simplerunloop just to let everyone more clear understanding of runloop principle, and will not put into actual use.
2, Simplerunloop in the Executeonce function must be _timerqueue copy to Enumarry, because in
It is not possible to modify the number of tuples in an array during traversal. This way, if you can continue to create a new timerfire in Viewcontroller
The Simpletimer event source is added to the queue. Or Viewcontroller Timerfire continues to invoke the
[Simplerunloop Rununtildate] Creates a sub-loop of Simplerunloop. At this point, if you add the Simpletimer event source to Simplerunloop,
An event-triggered call is called in this loop. Is the same as in the previous article Nsrunloop.
3, the Nsrunloop implementation of the system is certainly not so simple. So what's the difference between our simplerunloop and Nsrunloop, I think I have the following:
The input source for the input event source Simplerunloop can only be addtimer. While the system has port-based sources automatically sent by the kernel, Custom Input sources need to be sent manually from other threads. This is the key, if we can do these two actually can be a real nsrunloop. This can continue in-depth study.
Explore Runloop (ii) (Runloop self-fulfilling runloop)