NSOperation synchronization problem
I am struggling to find a way to synchronize operation on IPhone app. I have three main NSOperation.
NSInvocationOperation *showSpinner = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(spinnerOn:) object:YES];
NSInvocationOperation *reloadDatasource = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(reloadDatasource) object:nil];
NSInvocationOperation *hideSpinner = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(spinnerOn:) object:NO];
// Add dependency
[reloadDatasource addDependency:showSpinner];
[hideSpinner addDependency:reloadDatasource];
[self.queue addOperation:showSpinner];
[self.queue addOperation:reloadDatasource];
[self.queue addOperation:hideSpinner];
I can see that the three operations are correctly started in sequence. However, as you can imagine, the first operation create a UIView and attach it on top, while the last one should remove it.
It happens that graphically speaking the operations happens at once on the s开发者_运维知识库creen. So I can see the table already loaded, while the spinner is on screen, or other strange unsynchronized things.
I understood that change on the graphic side happen on the main thread. So I am asking how can I modify the code to do what it is supposed to do. Which is: create spinner, load data, and remove spinner ? Is there a common way to separate graphic operation and data operation ? For example create two distinct operation.
thanks
How about writing
[self spinnerOn:YES];
[self performSelectorInBackground:@selector(reloadDatasource) withObject:nil];
and then make call back to your main thread in 'reloadDataSource' method with
[self performSelectorInMainThread:@selector(spinnerOn:) withObject:NO];
Is it obligatory to use the NSOperation? If not, then I guess you are choosing the hard way to solve the simple problem, use the delegate of the NSConnection (or something similar as I am not sure what does your reloadDataSource do) to start and stop the spinner and then you'are done.
In your spinnerOn: method, can you try adding a bit of logic to make sure the operation occurs on the main thread?
if ( ![NSThread isMainThread] ) { [self performSelectorOnMainThread:@selector(spinnerOn:) withObject:anObject waitUntilDone:NO]; return; }
I'd be curious to see if that makes a difference. My suspicion is that if you call a UI task from an alternate thread, those get queued up and might end up happening all at once at a later, undefined time on the main thread.
Another option to try is making your operation queue a serial queue. By this I mean have it only perform one task at a time. Then you can forget about your dependencies as it will always just execute tasks in the order that you add them. You can try that by setting it:
[self.queue setMaxConcurrentOperationCount:1];
Let me know if this is helpful at all.
精彩评论