Created
June 4, 2014 18:04
-
-
Save farktronix/92c3b1a1901eec6c6f2e to your computer and use it in GitHub Desktop.
dispatch_group based state machine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// StateMachineExample | |
// | |
// Created by Jacob Farkas on 6/4/14. | |
// | |
#import <Foundation/Foundation.h> | |
#define DownloadTempPath "/tmp/tacosXXXXXX" | |
typedef NS_ENUM(NSUInteger, MyOperationState) { | |
MyOperationStateNotStarted = 1, | |
MyOperationStateDownloadingPages, | |
MyOperationStateProcessingData, | |
MyOperationStateComplete = UINT_MAX, | |
}; | |
@interface FindNewlinesOperation : NSOperation | |
- (instancetype)initWithURLsToDownload:(NSArray *)urlsToDownload; | |
@property (nonatomic, strong) NSArray /* NSURL* */ *urlsToDownload; | |
@property (nonatomic, copy) void (^pagesFetchedBlock)(NSUInteger newlineCount); | |
@end | |
@interface FindNewlinesOperation () | |
@property (nonatomic, assign) MyOperationState state; | |
@property (nonatomic, strong) dispatch_group_t stateTransitionGroup; | |
@property (nonatomic, assign) BOOL isFinished; | |
@property (nonatomic, assign) BOOL isExecuting; | |
@property (nonatomic, strong) NSMutableArray *downloadTasks; | |
@property (nonatomic, strong) NSMutableArray /* NSFileHandle */ *downloadedFiles; | |
@property (atomic, assign) NSUInteger newlineCount; | |
@end | |
@implementation FindNewlinesOperation | |
- (instancetype)initWithURLsToDownload:(NSArray *)urlsToDownload { | |
if ((self = [super init])) { | |
_state = MyOperationStateNotStarted; | |
_stateTransitionGroup = dispatch_group_create(); | |
_urlsToDownload = urlsToDownload; | |
_downloadTasks = [NSMutableArray new]; | |
_downloadedFiles = [NSMutableArray new]; | |
} | |
return self; | |
} | |
- (void)setIsFinished:(BOOL)isFinished { | |
[self willChangeValueForKey:@"isFinished"]; | |
_isFinished = isFinished; | |
[self didChangeValueForKey:@"isFinished"]; | |
} | |
- (void)setIsExecuting:(BOOL)isExecuting { | |
[self willChangeValueForKey:@"isExecuting"]; | |
_isExecuting = isExecuting; | |
[self didChangeValueForKey:@"isExecuting"]; | |
} | |
// Automatically advances the state machine to the next state when everything has left the dispatch group | |
// This only needs to be called once from the start method. | |
- (void)makeStateTransition { | |
switch(self.state) { | |
case MyOperationStateNotStarted: | |
self.state = MyOperationStateDownloadingPages; | |
[self downloadPages]; | |
break; | |
case MyOperationStateDownloadingPages: | |
self.state = MyOperationStateProcessingData; | |
[self processData]; | |
break; | |
case MyOperationStateProcessingData: | |
case MyOperationStateComplete: | |
if (self.pagesFetchedBlock) self.pagesFetchedBlock(self.newlineCount); | |
self.isExecuting = NO; | |
self.isFinished = YES; | |
break; | |
} | |
if (!self.isFinished) { | |
dispatch_group_notify(self.stateTransitionGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
[self makeStateTransition]; | |
}); | |
} | |
} | |
- (void)start { | |
self.isFinished = NO; | |
self.isExecuting = YES; | |
[self makeStateTransition]; | |
} | |
- (void)main { | |
[self start]; | |
} | |
- (void)downloadPages { | |
for (NSURL *url in self.urlsToDownload) { | |
dispatch_group_enter(self.stateTransitionGroup); | |
NSURLSessionDownloadTask *downloadTask = [[NSURLSession sharedSession] downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) { | |
if (location) { | |
int fd = open([location fileSystemRepresentation], O_RDONLY); | |
if (fd >= 0) { | |
NSFileHandle *fh = [[NSFileHandle alloc] initWithFileDescriptor:fd closeOnDealloc:YES]; | |
@synchronized(self.downloadedFiles) { | |
[self.downloadedFiles addObject:fh]; | |
} | |
} | |
} | |
dispatch_group_leave(self.stateTransitionGroup); | |
}]; | |
[self.downloadTasks addObject:downloadTask]; | |
[downloadTask resume]; | |
} | |
} | |
- (void)processData { | |
for (NSFileHandle *fh in self.downloadedFiles) { | |
dispatch_group_enter(self.stateTransitionGroup); | |
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ | |
NSData *fileData = [fh readDataOfLength:getpagesize()]; | |
while ([fileData length]) { | |
for (char *c = (char *)[fileData bytes]; c < (char *)[fileData bytes] + [fileData length]; c++) { | |
if (*c == '\n') self.newlineCount++; | |
} | |
fileData = [fh readDataOfLength:getpagesize()]; | |
} | |
dispatch_group_leave(self.stateTransitionGroup); | |
}); | |
} | |
} | |
@end | |
int main(int argc, const char * argv[]) { | |
@autoreleasepool { | |
NSMutableArray *urlsToDownload = [NSMutableArray new]; | |
NSArray *args = [[NSProcessInfo processInfo] arguments]; | |
for (NSString *urlString in [args subarrayWithRange:NSMakeRange(1, [args count] - 1)]) { | |
@try { | |
NSURL *curURL = [NSURL URLWithString:urlString]; | |
if (curURL) { | |
[urlsToDownload addObject:curURL]; | |
} | |
} @catch (NSException *e) { } | |
} | |
FindNewlinesOperation *op = [[FindNewlinesOperation alloc] initWithURLsToDownload:urlsToDownload]; | |
op.pagesFetchedBlock = ^(NSUInteger newlineCount) { | |
NSLog(@"%ld newlines were found on the URLs you provided", newlineCount); | |
}; | |
op.completionBlock = ^{ | |
exit(0); | |
}; | |
[[NSOperationQueue mainQueue] addOperation:op]; | |
dispatch_main(); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment