I have one big task that consist of several subtasks. and I want to add progress reporting for that big task.
for that I want to use NSProgress
, and according to the class documentation I can do this kind of subtask progress by using its child - parent mechanism.
So to simplify it, lets say I have big task that consist of one subtask (of course in real life there would be more subtasks). So this is what I have done:
#define kFractionCompletedKeyPath @"fractionCompleted"
- (void)runBigTask {
_progress = [NSProgress progressWithTotalUnitCount:100]; // 100 is arbitrary
[_progress addObserver:self
forKeyPath:kFractionCompletedKeyPath
options:NSKeyValueObservingOptionNew
context:NULL];
[_progress becomeCurrentWithPendingUnitCount:100];
[self subTask];
[_progress resignCurrent];
}
- (void)subTask {
NSManagedObjectContext *parentContext = self.managedObjectContext; // self is AppDelegate in this example
NSManagedObjectContext *bgContext = [[NSManagedObjectContext alloc]initWithConcurrencyType:NSPrivateQueueConcurrencyType];
[bgContext setParentContext:parentContext];
[bgContext performBlockAndWait:^{
NSInteger totalUnit = 1000;
NSInteger completedUnits = 0;
NSProgress *subProgress = [NSProgress progressWithTotalUnitCount:totalUnit];
for (int i=0; i < totalUnit; i++) {
// run some Core Data related code...
completedUnits++;
subProgress.completedUnitCount = completedUnits;
}
}];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:kFractionCompletedKeyPath]) {
if ([object isKindOfClass:[NSProgress class]]) {
NSProgress *progress = (NSProgress *)object;
NSLog(@"progress… %f", progress.fractionCompleted);
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
As you can see, the subtask uses background context to run some Core Data related code, and the background context uses the main context as his parent context.
This causes some weird KVO of the progress's "fractionCompleted" property.
this is the print:
progress… 1.000000 // why???
progress… 0.500000 // why?????
progress… 1.000000 // why???????
progress… 0.666650 // why???????????
progress… 0.666990
progress… 0.667320
progress… 0.667660
progress… 0.667990
progress… 0.668320
...
progress… 1.000000
As you can see the print starts with 1.0, 0.5 and 1.0 and then is goes to 0.66 ?!
from here it acts normal and goes to 1.0 like I expect.
I tried to understand why this is happening, and I noticed that if I remove the parent context from the background context, it works fine! I get progress from 0.0 to 1.0.
Any ideas why is this happening? and how can I fix that?
I added a very simple project to demonstrate this issue (you can remove the setParentContext: call to see that it works well without it)