I have an ObjC class (Writer) that has some properties and it writes to these properties on main thread. Then there is other class (Reader) that reads these properties on main thread or background thread.
When reader reads from main thread it gets the exact values these properties in Writer class hold, but when reader reads from background thread there is obviously no guaranty reader will get the same values. Here is the example code.
Here in Writer class
In Reader class:
I want to make sure that numberSum is indeed sum of all ones in numberString variable.
// Writer Class
@interface Writer : NSObject
@property (nonatomic,strong) NSString* numberString;
@property (nonatomic,assign) NSUInteger numberSum;
@end
@implementation Writer
-(instancetype)init {
if (self = [super init]){
self.numberString = @"1";
self.numberSum = 1;
self.timer = [NSTimer timerWithTimeInterval:0.1f target:self selector:@selector(writeToVariables:) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
return self;
}
- (void)writeToVariables:(NSTimer *)timer {
self.numberString = [self.numberString stringByAppendingString:@",1"];
self.numberSum += 1;
}
// Reader Class
@interface Reader ()
@property (nonatomic,strong) Writer* writer;
@end
@implementation Reader
-(instancetype)init {
if (self = [super init]){
self.writer = [Writer new];
}
return self;
}
-(void) getVariablesOnBackground:(BOOL)background {
void (^readerBlock)() = ^{
NSString* numberStr = self.writer.numberString;
// Introduce delay So background thread get time to update numberSum
int i=0;
while (i < 10000000) {
i += 1;
}
NSUInteger numberSum = self.writer.numberSum;
NSArray *onesArray = [numberStr componentsSeparatedByString:@","];
NSLog(@"%lu",(unsigned long)onesArray.count);
NSLog(@"%lu",(unsigned long)numberSum);
};
// If background then dispatch on default queue else just call the block
if (background) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0),readerBlock);
}
else {
readerBlock();
}
}
@end
This is demo code just for example purpose. Two NSLog statements in the readerBlock will always print same when executed in main thread, but in background thread they will be different some time, as by the time numberSum is read in Reader after reading numberString it has been updated by the main thread in Writer.
What should I do to make sure I always get right values irrespective of calling thread?
What you need is combination of concurrent dispatch queue and dispatch barrier functions. As long, as you need to access both numberString
and numberSum
simultaneously, you should hide it and give single method to get them both. Like this
@interface Writer : NSObject {
NSString* numberString;
NSUInteger numberSum;
dispatch_queue_t accessQueue;
}
- (NSDicionary*) readStringAndSum;
@end
@implementation Writer
- (instancetype) init {
...
accessQueue = dispatch_queue_create("Access queue", DISPATCH_QUEUE_CONCURRENT);
...
}
- (void)writeToVariables:(NSTimer *)timer {
dispatch_barrier_async(accessQueue, ^{
numberString = [self.numberString stringByAppendingString:@",1"];
numberSum += 1;
});
}
- (NSDicionary*) readStringAndSum; {
__block NSDictionary* response;
dispatch_sync(accessQueue ^{
response = @{@"Sum" : [NSNumber numberWithUnsignedInteger: numberSum],
@"String" : [numberString copy];
}
});
return response;
}
@end