diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c675cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +testAsyncNetworking +testDispatch +testMultiTimers \ No newline at end of file diff --git a/AsyncDownloader.h b/AsyncDownloader.h index e237b57..b9eed55 100644 --- a/AsyncDownloader.h +++ b/AsyncDownloader.h @@ -6,7 +6,7 @@ * Licensed under the MIT License. */ -#import +@import Foundation; // Notification name extern NSString * const AsyncDownloaderFinishedNotification; @@ -15,18 +15,17 @@ extern NSString * const AsyncDownloaderFinishedNotification; extern NSString * const AsyncDownloaderFinishedNotificationSucceededKey; // @(YES) or @(NO) extern NSString * const AsyncDownloaderFinishedNotificationErrorKey; // NSError or missing -@interface AsyncDownloader : NSObject { - NSURLConnection *_connection; - NSMutableData *_responseData; -} +@interface AsyncDownloader : NSObject -@property (getter=isCancelled) BOOL cancel; -@property (readonly) NSURL *url; -@property (readonly) NSData *responseData; -@property (readonly) NSStringEncoding responseEncoding; -@property (readonly, getter=isFinished) BOOL finished; -@property (readonly) NSError *error; +@property (getter=isCancelled) BOOL cancel; +@property (getter=isFinished,readonly) BOOL finished; -- (BOOL)downloadFromURL:(NSURL *)url; +@property (readonly) NSStringEncoding responseEncoding; + +@property (readonly) NSURL * url; +@property (readonly) NSData * responseData; +@property (readonly) NSError * error; + +- (BOOL) downloadFromURL:(NSURL*)url; @end diff --git a/AsyncDownloader.m b/AsyncDownloader.m index 2396106..c7400ae 100644 --- a/AsyncDownloader.m +++ b/AsyncDownloader.m @@ -14,35 +14,29 @@ #define LOG(fmt, ...) /* nothing */ #endif -NSString * const AsyncDownloaderFinishedNotification = @"AsyncDownloaderFinishedNotification"; -NSString * const AsyncDownloaderFinishedNotificationSucceededKey = @"AsyncDownloaderFinishedNotificationSucceeded"; -NSString * const AsyncDownloaderFinishedNotificationErrorKey = @"AsyncDownloaderFinishedNotificationError"; -static NSString * const _asyncDownloaderErrorDomain = @"AsyncDownloaderErrorDomain"; - -// Private Methods -@interface AsyncDownloader () +NSString * const AsyncDownloaderFinishedNotification = @"AsyncDownloaderFinishedNotification", + * const AsyncDownloaderFinishedNotificationSucceededKey = @"AsyncDownloaderFinishedNotificationSucceeded", + * const AsyncDownloaderFinishedNotificationErrorKey = @"AsyncDownloaderFinishedNotificationError"; -- (void)_sendFinishedNotification; -- (void)_setErrorWithString:(NSString *)message - underlyingError:(NSError *)underlyingError; - -@end +static NSString * const _asyncDownloaderErrorDomain = @"AsyncDownloaderErrorDomain"; @implementation AsyncDownloader +{ + NSURLConnection *_connection; + NSMutableData *_responseData; +} -@synthesize cancel = _cancel; -@synthesize url = _url; -@synthesize responseEncoding = _responseEncoding; -@synthesize finished = _finished; -@synthesize error = _error; +@synthesize url = _url, error = _error, cancel = _cancel, + finished = _finished, responseEncoding = _responseEncoding; - (BOOL)downloadFromURL:(NSURL *)url { - _url = url; - _connection = nil; - _responseData = nil; - _cancel = NO; + + _url = url; + _connection = nil; + _responseData = nil; + _cancel = NO; _responseEncoding = NSUTF8StringEncoding; - _error = nil; + _error = nil; NSURLRequest *request = [NSURLRequest requestWithURL:url]; if (!request) { @@ -53,8 +47,8 @@ - (BOOL)downloadFromURL:(NSURL *)url { return NO; } - _connection = [[NSURLConnection alloc] initWithRequest:request - delegate:self]; + _connection = [NSURLConnection.alloc initWithRequest:request delegate:self]; + if (!_connection) { _finished = YES; [self _setErrorWithString:@"Failed to create NSURLConnection object" @@ -70,36 +64,33 @@ - (BOOL)downloadFromURL:(NSURL *)url { #pragma mark - NSURLConnectionDelegate methods -- (void)connection:(NSURLConnection *)connection -didReceiveResponse:(NSURLResponse *)response { +- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response { - if (_cancel) - return; + if (_cancel) return; NSString *textEncodingName = response.textEncodingName; LOG(@"Received response from '%@'. Encoding=%@", _url, textEncodingName); - _responseData = [NSMutableData new]; - if (textEncodingName) { - CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)textEncodingName); - _responseEncoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding); - } else { - _responseEncoding = NSUTF8StringEncoding; - } + _responseData = NSMutableData.new; + _responseEncoding = textEncodingName + ? CFStringConvertEncodingToNSStringEncoding( + CFStringConvertIANACharSetNameToEncoding( + (CFStringRef)textEncodingName + ) + ) + : NSUTF8StringEncoding; } -- (void)connection:(NSURLConnection *)connection - didReceiveData:(NSData *)data { +- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data { - if (_cancel) - return; + if (_cancel) return; LOG(@"Received %lu bytes from '%@'", (unsigned long)[data length], _url); [_responseData appendData:data]; } -- (NSCachedURLResponse *)connection:(NSURLConnection *)connection - willCacheResponse:(NSCachedURLResponse *)cachedResponse { +- (NSCachedURLResponse *)connection:(NSURLConnection*)connection + willCacheResponse:(NSCachedURLResponse*)cachedResponse { return nil; // Not necessary } @@ -120,28 +111,29 @@ - (void)connection:(NSURLConnection *)connection [self _sendFinishedNotification]; } +#pragma mark - Private Methods + - (void)_sendFinishedNotification { NSAssert(_finished, @"Download didn't finish!"); - NSMutableDictionary *userInfo = [NSMutableDictionary new]; - if (_error) { - userInfo[AsyncDownloaderFinishedNotificationSucceededKey] = @(NO); - userInfo[AsyncDownloaderFinishedNotificationErrorKey] = _error; - } else { - userInfo[AsyncDownloaderFinishedNotificationSucceededKey] = @(YES); - } + id userInfo = + @{AsyncDownloaderFinishedNotificationSucceededKey : @(!(_error))}.mutableCopy; + + if (_error) + [userInfo setObject:_error forKey:AsyncDownloaderFinishedNotificationErrorKey]; - [[NSNotificationCenter defaultCenter] postNotificationName:AsyncDownloaderFinishedNotification - object:self + [NSNotificationCenter.defaultCenter postNotificationName:AsyncDownloaderFinishedNotification + object:self userInfo:userInfo]; } - (void)_setErrorWithString:(NSString *)message underlyingError:(NSError *)underlyingError { - NSMutableDictionary *userInfo = [NSMutableDictionary new]; - if (message) - userInfo[NSLocalizedDescriptionKey] = message; + NSMutableDictionary *userInfo = NSMutableDictionary.new; + + if (message) userInfo[NSLocalizedDescriptionKey] = message; + if (underlyingError) userInfo[NSUnderlyingErrorKey] = underlyingError; diff --git a/Makefile b/Makefile index 0aa5ce9..b7843c5 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,6 @@ CC = clang +CFLAGS = -fmodules MFLAGS = -DLOGGING -DDEBUG=1 -g -fobjc-arc LDFLAGS = -g LIBS = -framework Foundation @@ -9,18 +10,18 @@ SRCS = $(wildcard *.m) OBJS = $(SRCS:.m=.o) %.o: %.m - $(CC) $(MFLAGS) -c -o $@ $< + $(CC) $(CFLAGS) $(MFLAGS) -c -o $@ $< all: $(TARGETS) testAsyncNetworking: testAsyncNetworking.o RunLoopController.o AsyncDownloader.o - $(CC) $(LDFLAGS) -o $@ testAsyncNetworking.o RunLoopController.o AsyncDownloader.o $(LIBS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ testAsyncNetworking.o RunLoopController.o AsyncDownloader.o $(LIBS) testMultiTimers: testMultiTimers.o RunLoopController.o - $(CC) $(LDFLAGS) -o $@ testMultiTimers.o RunLoopController.o $(LIBS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ testMultiTimers.o RunLoopController.o $(LIBS) testDispatch: testDispatch.o RunLoopController.o - $(CC) $(LDFLAGS) -o $@ testDispatch.o RunLoopController.o $(LIBS) + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ testDispatch.o RunLoopController.o $(LIBS) test: $(TARGETS) ./testAsyncNetworking -s http://www.google.co.uk http://facebook.com http://stackoverflow.com http://qwerty diff --git a/RunLoopController.h b/RunLoopController.h index f335991..232d0d9 100644 --- a/RunLoopController.h +++ b/RunLoopController.h @@ -6,88 +6,67 @@ * Licensed under the MIT License. */ -#import +@import Foundation; -@interface RunLoopController : NSObject { - NSMachPort *_terminatePort; - BOOL _terminate; -} +@interface RunLoopController : NSObject -/** - * Get the number of registered instances. - * - * This value can be used to decide to terminate the main thread when no - * more worker threads exist. - * - * @return The number of registered instances. +/*! Set to YES to set terminate flag and signal the associated + run loop (calling the @c -signal method). + @return YES if the run loop terminate flag/should terminate, else NO. */ -+ (NSInteger)instanceCount; +@property (nonatomic) BOOL shouldTerminate; -/** - * Retrieve the RunLoopController object associated with the current thread. - * - * @return The RunLoopController object associated with the current thread. +/*! The number of registered instances. + @note This value can be used to decide to terminate + the main thread when no more worker threads exist. */ -+ (RunLoopController *)currentRunLoopController; ++ (NSInteger) instanceCount; -/** - * Retrieve the RunLoopController object associated with the main thread. - * - * @return The RunLoopController object associated with the main thread. - */ -+ (RunLoopController *)mainRunLoopController; +/// The RunLoopController object associated with the current thread. -/** - * Register the run loop controller with the current run loop. - */ -- (void)register; ++ (RunLoopController*) currentRunLoopController; -/** - * Deregister the run loop controller from the current run loop. - */ -- (void)deregister; +/// The RunLoopController object associated with the main thread. -/** - * Run the current run loop. This is a shortcut for: - * calling runMode:NSDefaultRunLoopModebeforeDate:[NSDate distantFuture] - * - * @return NO if the run loop was asked to terminate, or an error occurred. YES if - * the run loop finished for another reason. - */ -- (BOOL)run; ++ (RunLoopController*) mainRunLoopController; -/** - * Run the current run loop. - * - * @param mode As per [NSRunLoop runMode:limitDate:]. - * @param limitDate As per [NSRunLoop runMode:limitDate:]. - * - * @return NO if the run loop was asked to terminate, or an error occurred. YES if - * the run loop finished for another reason. - */ -- (BOOL)runMode:(NSString *)mode - beforeDate:(NSDate *)limitDate; +/// Register the run loop controller with the current run loop. + +- (void) register; + +/// Deregister the run loop controller from the current run loop. -/** - * Set the terminate flag and signal the associated run loop (calling the - * -signal method). +- (void) deregister; + +/*! Run the current run loop. + @note This is a shortcut for: + @see runMode:NSDefaultRunLoopModebeforeDate: with NSDate.distantFuture + @return NO if the run loop was asked to terminate, or an error occurred. + YES if the run loop finished for another reason. */ -- (void)terminate; +- (BOOL) run; -/** - * Determine if the terminate flag is set. - * - * @return YES if the run loop should terminate, else NO. +/*! Run the current run loop. + @param mode As per [NSRunLoop runMode:limitDate:]. + @param limitDate As per [NSRunLoop runMode:limitDate:]. + @return NO the run loop was asked to terminate, or an error occurred. + YES the run loop finished for another reason. */ -- (BOOL)shouldTerminate; +- (BOOL) runMode:(NSString*)mode beforeDate:(NSDate*)limitDate; -/** - * Signal the run loop associated with the run loop controller. - * - * This is useful if you want to wake up a run loop so the code running the loop - * can evaluate conditions for termination (normally the main thread run loop). +/*! Signal the run loop associated with the run loop controller. + @note This is useful if you want to wake up a run loop so the code running the loop + can evaluate conditions for termination (normally the main thread run loop). */ -- (void)signal; +- (void) signal; @end + +#ifdef WANT_RUNLOOP_LOGGER +#ifdef LOGGING +#define LOG(fmt, ...) NSLog(fmt, ## __VA_ARGS__) +#else +#define LOG(fmt, ...) /* nothing */ +#endif +#endif \ No newline at end of file diff --git a/RunLoopController.m b/RunLoopController.m index 8189959..6b4fe9f 100644 --- a/RunLoopController.m +++ b/RunLoopController.m @@ -6,45 +6,42 @@ * Licensed under the MIT License. */ -#import "RunLoopController.h" - -#ifdef LOGGING -#define LOG(fmt, ...) NSLog(fmt, ## __VA_ARGS__) -#else -#define LOG(fmt, ...) /* nothing */ +#ifndef WANT_RUNLOOP_LOGGER +#define WANT_RUNLOOP_LOGGER #endif +#import "RunLoopController.h" + static NSString * const _threadDictKey = @"RunLoopController"; static NSInteger _instanceCount = 0; -@implementation RunLoopController +@implementation RunLoopController { NSMachPort *_terminatePort; } #pragma mark - RunLoopController methods + (RunLoopController *)currentRunLoopController { - return [[[NSThread currentThread] threadDictionary] objectForKey:_threadDictKey]; + + return NSThread.currentThread.threadDictionary[_threadDictKey]; } + (RunLoopController *)mainRunLoopController { - return [[[NSThread mainThread] threadDictionary] objectForKey:_threadDictKey]; -} -+ (NSInteger)instanceCount { - return _instanceCount; + return NSThread.mainThread.threadDictionary[_threadDictKey]; } -- (void)register { ++ (NSInteger)instanceCount { return _instanceCount; } - NSAssert(![RunLoopController currentRunLoopController], @"Already registered"); +- (void) register { - NSRunLoop *runloop = [NSRunLoop currentRunLoop]; - _terminatePort = [NSMachPort new]; + NSAssert(!RunLoopController.currentRunLoopController, @"Already registered"); + + NSRunLoop *runloop = NSRunLoop.currentRunLoop; + _terminatePort = NSMachPort.new; _terminatePort.delegate = self; - [runloop addPort:_terminatePort - forMode:NSDefaultRunLoopMode]; - NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary]; - [threadDict setObject:self forKey:_threadDictKey]; + [runloop addPort:_terminatePort forMode:NSDefaultRunLoopMode]; + + NSThread.currentThread.threadDictionary[_threadDictKey] = self; _instanceCount++; @@ -53,72 +50,56 @@ - (void)register { - (void)deregister { - NSAssert([RunLoopController currentRunLoopController], @"Not registered"); + NSAssert(RunLoopController.currentRunLoopController, @"Not registered"); _instanceCount--; - NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary]; - [threadDict removeObjectForKey:_threadDictKey]; + [NSThread.currentThread.threadDictionary removeObjectForKey:_threadDictKey]; - NSRunLoop *runloop = [NSRunLoop currentRunLoop]; - [runloop removePort:_terminatePort - forMode:NSDefaultRunLoopMode]; + NSRunLoop *runloop = NSRunLoop.currentRunLoop; + + [runloop removePort:_terminatePort forMode:NSDefaultRunLoopMode]; [_terminatePort invalidate]; _terminatePort = nil; LOG(@"%p: deregister. instanceCount=%ld", self, (long)_instanceCount); } -- (BOOL)run { - return [self runMode:NSDefaultRunLoopMode - beforeDate:[NSDate distantFuture]]; -} +- (BOOL)run { return [self runMode:NSDefaultRunLoopMode beforeDate:NSDate.distantFuture]; } -- (BOOL)runMode:(NSString *)mode - beforeDate:(NSDate *)limitDate { +- (BOOL)runMode:(NSString*)mode beforeDate:(NSDate*)limitDate { - NSRunLoop *runloop = [NSRunLoop currentRunLoop]; - if (![runloop runMode:mode - beforeDate:limitDate]) { - LOG(@"%p: Error in [NSRunLoop runMode:beforeDate:]", self); - return NO; - } - - return !_terminate; + return ![NSRunLoop.currentRunLoop runMode:mode beforeDate:limitDate] + ? ({ LOG(@"%p: Error in [NSRunLoop runMode:beforeDate:]", self); NO; }) + : !self.shouldTerminate; } -- (void)terminate { +- (void) setShouldTerminate:(BOOL)shouldTerminate { + + if (shouldTerminate) { LOG(@"%p: Terminating", self); - _terminate = YES; + _shouldTerminate = shouldTerminate; [self signal]; // If we are not the main thread, then find the RunLoopController for the main thread // and also signal it's mach port to wake up its run loop - if (![NSThread isMainThread]) { - LOG(@"%p: Signalling main thread's run loop controller", self); + if (NSThread.isMainThread) return; - RunLoopController *runLoopController = [RunLoopController mainRunLoopController]; - [runLoopController signal]; - } -} + LOG(@"%p: Signalling main thread's run loop controller", self); + [RunLoopController.mainRunLoopController signal]; -- (BOOL)shouldTerminate { - return _terminate; + } } -- (void)signal { - [_terminatePort sendBeforeDate:[NSDate date] - components:nil - from:nil - reserved:0]; +- (void) signal { + + [_terminatePort sendBeforeDate:NSDate.date components:nil from:nil reserved:0]; } #pragma mark - NSMachPortDelegate methods -- (void)handleMachMessage:(void *)machMessage { - LOG(@"%p: Mach message received", self); -} +- (void) handleMachMessage:(void*)machMessage { LOG(@"%p: Mach message received", self); } @end diff --git a/testDispatch.m b/testDispatch.m index 997d6c6..ae0165f 100644 --- a/testDispatch.m +++ b/testDispatch.m @@ -6,19 +6,18 @@ * Licensed under the MIT License. */ -#import "RunLoopController.h" - -#ifdef LOGGING -#define LOG(fmt, ...) NSLog(fmt, ## __VA_ARGS__) -#else -#define LOG(fmt, ...) /* nothing */ +#ifndef WANT_RUNLOOP_LOGGER +#define WANT_RUNLOOP_LOGGER #endif +#import "RunLoopController.h" + int main(int argc, const char **argv) { + int retval = 0; @autoreleasepool { - RunLoopController *runLoopController = [RunLoopController new]; + RunLoopController *runLoopController = RunLoopController.new; [runLoopController register]; __block NSInteger count = 0; diff --git a/testMultiTimers.m b/testMultiTimers.m index c87974e..896e40d 100644 --- a/testMultiTimers.m +++ b/testMultiTimers.m @@ -6,28 +6,24 @@ * Licensed under the MIT License. */ +#ifndef WANT_RUNLOOP_LOGGER +#define WANT_RUNLOOP_LOGGER +#endif + #import "RunLoopController.h" -#ifdef LOGGING -#define LOG(fmt, ...) NSLog(fmt, ## __VA_ARGS__) -#else -#define LOG(fmt, ...) /* nothing */ -#endif +@interface TimerThread : NSThread -@interface TimerThread : NSThread { - NSTimer *_timer; -} -@property NSTimeInterval sleepTime; -@property RunLoopController *runLoopController; -@property (readonly, assign) BOOL finished; -@end +@property NSTimer * timer; +@property NSTimeInterval sleepTime; +@property RunLoopController * runLoopController; -@implementation TimerThread +@end -@synthesize sleepTime = _sleepTime; -@synthesize finished = _finished; +@implementation TimerThread @synthesize finished = _finished; - (void)main { + LOG(@"%p: Starting", self); self.runLoopController = [RunLoopController new]; @@ -48,7 +44,7 @@ - (void)main { - (void)timerFired:(NSTimer *)timer { LOG(@"%p: Fired", self); _finished = YES; - [self.runLoopController terminate]; + [self.runLoopController setShouldTerminate:YES]; } @end @@ -80,8 +76,7 @@ int main(int argc, const char **argv) { } } - if ([RunLoopController instanceCount] == 1) - break; + if (RunLoopController.instanceCount == 1) break; } [runLoopController deregister];