Created
September 4, 2011 10:00
-
-
Save sumerman/1192607 to your computer and use it in GitHub Desktop.
Intent of this code is to guide protected concurrent access to collection of states.
This file contains 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
#import <Foundation/Foundation.h> | |
typedef id (^MVGetterBlock)(void); | |
typedef void (^MVSetterBlock)(id); | |
typedef id (^MVStateOpBlock)(id); | |
typedef NSOperation * (^MVOpSetupBlock)(MVGetterBlock, MVSetterBlock); | |
@interface MVStateStorage : NSObject { | |
__block NSMutableDictionary *data; | |
__block NSMutableDictionary *queues; | |
} | |
- (void)scheduleBlock:(MVStateOpBlock)block forKey:(id)key; | |
- (void)setupOperationForKey:(id)key withBlock:(MVOpSetupBlock)block; | |
- (void)cancelOperationsForKey:(id)key; | |
- (NSDictionary *)dictionaryRepresentation; | |
- (NSArray *)allKeys; | |
@end |
This file contains 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
#import "MVStateStorage.h" | |
@implementation MVStateStorage | |
- (id)init { | |
self = [super init]; | |
if (self) { | |
data = [[NSMutableDictionary alloc] init]; | |
queues = [[NSMutableDictionary alloc] init]; | |
} | |
return self; | |
} | |
- (void)dealloc { | |
[queues enumerateKeysAndObjectsUsingBlock:^(id key, NSOperationQueue *obj, BOOL *s) { | |
[obj cancelAllOperations]; | |
[obj waitUntilAllOperationsAreFinished]; | |
[obj setSuspended:YES]; | |
}]; | |
[queues release]; | |
[data release]; | |
[super dealloc]; | |
} | |
- (NSOperationQueue *)queueForKey:(id)key { | |
NSOperationQueue *q = [queues objectForKey:key]; | |
if (!q) { | |
@synchronized(queues) { | |
q = [[NSOperationQueue alloc] init]; | |
[q setMaxConcurrentOperationCount:1]; | |
[queues setObject:q forKey:key]; | |
[q release]; | |
} | |
} | |
return q; | |
} | |
- (void)cancelOperationsForKey:(id)key { | |
[[self queueForKey:key] cancelAllOperations]; | |
} | |
- (void)scheduleBlock:(MVStateOpBlock)block forKey:(id)key { | |
NSOperationQueue *q = [self queueForKey:key]; | |
[q addOperationWithBlock:^{ | |
id val = nil; | |
id res = nil; | |
@synchronized(data) { | |
val = [data objectForKey:key]; | |
} | |
NSAutoreleasePool *p = [NSAutoreleasePool new]; | |
res = block(val); | |
if (val != res) { | |
if (!res) { | |
[[NSOperationQueue currentQueue] cancelAllOperations]; | |
@synchronized(data) { | |
[data removeObjectForKey:key]; | |
} | |
return; | |
} | |
else { | |
@synchronized(data) { | |
[data setObject:res forKey:key]; | |
} | |
} | |
} | |
[p release]; | |
}]; | |
} | |
#define PREVENT \ | |
if (preventAccess) {\ | |
[NSException raise:NSInternalInconsistencyException format:@"Setter function called in operation *setup* block."];\ | |
} | |
- (void)setupOperationForKey:(id)key withBlock:(MVOpSetupBlock)block { | |
NSOperationQueue *q = [self queueForKey:key]; | |
__block BOOL preventAccess = YES; | |
__block id cachedVal = nil; | |
MVGetterBlock getter = ^{ | |
PREVENT | |
if (cachedVal) return cachedVal; | |
@synchronized(data) { | |
return [data objectForKey:key]; | |
} | |
}; | |
MVSetterBlock setter = ^(id newVal) { | |
PREVENT | |
@synchronized(data) { | |
cachedVal = newVal; | |
[data setObject:newVal forKey:key]; | |
} | |
}; | |
NSAutoreleasePool *p = [NSAutoreleasePool new]; | |
NSOperation *op = block(getter, setter); | |
preventAccess = NO; | |
cachedVal = nil; | |
[q addOperation:op]; | |
[p release]; | |
} | |
#undef PREVENT | |
- (NSDictionary *)dictionaryRepresentation { | |
@synchronized(data) { | |
return [[[NSDictionary alloc] initWithDictionary:data copyItems:YES] autorelease]; | |
} | |
} | |
- (NSArray *)allKeys { | |
@synchronized(data) { | |
return [[[data allKeys] copy] autorelease]; | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment