- (void)onTimerTick:(NSTimer*)timer { NSLog(@"MY TIMER TICKED"); } - (void)testTimerBasics { NSLog(@"timer time"); [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(onTimerTick:) userInfo:nil repeats:YES]; [timer fire]; //manually calling fire DOES log 'MY TIMER TICKED' NSLog(@"about to wait"); [NSThread sleepForTimeInterval:2.0]; //absolutely no logs of 'MY TIMER TICKED' occur; somehow the time doesn't fire during a thread sleep :( NSLog(@"wait time is over"); }
Sadly absolutely no log messages are printed during our two second sleep ([NSThread sleepForTimeInterval:2.0]) ; WTF?!
After much Google and literally in the midst of typing a Stack Overflow question I came across a question involving waiting for something else that mentioned NSRunLoop in passing. The very existence of a run loop class suggests an answer: our tests run on the same thread as the run loop. This means if we put the run loop to sleep nothing gets processed. Instead of sleep we need some sort of "run the run loop for a while" approach. Luckily it turns out that NSRunLoop provides a runUntilDate API so we can re-write the test above as follows:
- (void)onTimerTick:(NSTimer*)timer { NSLog(@"MY TIMER TICKED"); } - (void)testTimerBasics { NSLog(@"timer time"); NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(onTimerTick:) userInfo:nil repeats:YES]; //[timer fire]; NSDate *runUntil = [NSDate dateWithTimeIntervalSinceNow: 3.0 ]; NSLog(@"about to wait"); [[NSRunLoop currentRunLoop] runUntilDate:runUntil]; NSLog(@"wait time is over"); }We've found the right magic incantation! Knuth would be proud.
Speaking of magic incantations, I am using the SyntaxHighlighter libraries hosted @ http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/. However, there is no Objectionable-C brush there so I took the one posted @ http://www.undermyhat.org/blog/wp-content/uploads/2009/09/shBrushObjectiveC.js and updated it the casing and namespace names to the newer highlighter standard. The updated brush looks like this:
dp.sh.Brushes.ObjC = function() { var datatypes = 'char bool BOOL double float int long short id void'; var keywords = 'IBAction IBOutlet SEL YES NO readwrite readonly nonatomic nil NULL '; keywords += 'super self copy '; keywords += 'break case catch class const copy __finally __exception __try '; keywords += 'const_cast continue private public protected __declspec '; keywords += 'default delete deprecated dllexport dllimport do dynamic_cast '; keywords += 'else enum explicit extern if for friend goto inline '; keywords += 'mutable naked namespace new noinline noreturn nothrow '; keywords += 'register reinterpret_cast return selectany '; keywords += 'sizeof static static_cast struct switch template this '; keywords += 'thread throw true false try typedef typeid typename union '; keywords += 'using uuid virtual volatile whcar_t while'; // keywords += '@property @selector @interface @end @implementation @synthesize '; this.regexList = [ { regex: dp.sh.RegexLib.SingleLineCComments, css: 'comments' }, // one line comments { regex: dp.sh.RegexLib.MultiLineCComments, css: 'comments' }, // multiline comments { regex: dp.sh.RegexLib.DoubleQuotedString, css: 'string' }, // double quoted strings { regex: dp.sh.RegexLib.SingleQuotedString, css: 'string' }, // single quoted strings { regex: new RegExp('^ *#.*', 'gm'), css: 'preprocessor' }, // preprocessor { regex: new RegExp(this.GetKeywords(datatypes), 'gm'), css: 'datatypes' }, // datatypes { regex: new RegExp(this.GetKeywords(keywords), 'gm'), css: 'keyword' }, // keyword { regex: new RegExp('\\bNS\\w+\\b', 'g'), css: 'keyword' }, // keyword { regex: new RegExp('@\\w+\\b', 'g'), css: 'keyword' }, // keyword ]; this.CssClass = 'dp-objc'; this.Style = '.dp-objc .datatypes { color: #2E8B57; font-weight: bold; }'; } dp.sh.Brushes.ObjC.prototype = new dp.sh.Highlighter(); dp.sh.Brushes.ObjC.Aliases = ['objc'];