views:

53

answers:

3

Hi All,

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 screen. 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

A: 

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.

sfa
+1  A: 

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];
tia
this seems to cause the same problem, change to UI happen unsynchronized in respect to the workflow.
Leonardo
Could you please elaborate how it is 'unsynchronized'?
tia
I had to change, the syntax, because it's wrong, and some other little change to the app methods to make it work. What I meant with 'unsynchronized' is to display the spinner, then the reloaded data, then finally removing the spinner. But all of the tasks should be synchronized. One after the other. I was thinking as NSQueue to best way to synchronize.
Leonardo
A: 

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.

Jackie Treehorn