I am writing an application that periodically retrieves data from a web server using ASI HTTP and then processes this data to display something that is relevant to the user in the user interface. Data is retrieved from different requests on the same server. The data itself must be processed in a specific order. One of the data blocks is much larger than the others.
In order not to block the user interface during data processing, I tried to use NSOperationQueue
to start processing data for different threads. This works great in about 90% of cases. However, in the remaining 10% of the time, the largest block of data is processed in the main stream, which forces the user interface to be blocked for 1-2 seconds. The application contains two MKMapViews on different tabs. When both MKMapViews tabs are loaded, the percentage of time that the largest block of data is processed on the main thread increases above 50% (which seems to indicate that this happens when there is more parallel activity).
Is there a way to prevent NSOperationQueue
code from running in the main thread?
I tried to play with NSOperationQueue
–setMaxConcurrentOperationCount:
increasing and decreasing it, but there were no real changes to this problem.
This is the code that runs the periodic update:
- (void)refreshAll{ // Create Operations ServerRefreshOperation * smallDataProcessor1Op = [[ServerRefreshOperation alloc] initWithDelegate:_smallDataProcessor1]; ServerRefreshOperation * smallDataProcessor2Op = [[ServerRefreshOperation alloc] initWithDelegate:_smallDataProcessor2]; ServerRefreshOperation * smallDataProcessor3Op = [[ServerRefreshOperation alloc] initWithDelegate:_smallDataProcessor3]; ServerRefreshOperation * smallDataProcessor4Op = [[ServerRefreshOperation alloc] initWithDelegate:_smallDataProcessor4]; ServerRefreshOperation * smallDataProcessor5Op = [[ServerRefreshOperation alloc] initWithDelegate:_smallDataProcessor5]; ServerRefreshOperation * hugeDataProcessorOp = [[ServerRefreshOperation alloc] initWithDelegate:_hugeDataProcessor]; // Create dependency graph (for response processing) [HugeDataProcessorOp addDependency:smallDataProcessor4Op.operation]; [smallDataProcessor5Op addDependency:smallDataProcessor4Op.operation]; [smallDataProcessor4Op addDependency:smallDataProcessor3Op.operation]; [smallDataProcessor4Op addDependency:smallDataProcessor2Op.operation]; [smallDataProcessor4Op addDependency:smallDataProcessor1Op.operation]; // Start be sending all requests to server (startAsynchronous directly calls the ASIHTTPRequest startAsynchronous method) [smallDataProcessor1Op startAsynchronous]; [smallDataProcessor2Op startAsynchronous]; [smallDataProcessor3Op startAsynchronous]; [smallDataProcessor4Op startAsynchronous]; [smallDataProcessor5Op startAsynchronous]; [hugeDataProcessorOp startAsynchronous]; }
This is the code that sets up the ASI HTTP termination block that starts data processing:
[_request setCompletionBlock:^{ [self.delegate setResponseString:_request.responseString]; [[MyModel queue] addOperation:operation];
I added this block of code to the entire NSInvocationOperation Invoked method at the entry point:
if([NSThread isMainThread]){ NSLog(@"****************************Running <operation x> on Main thread"); }
A line is printed every time the user interface freezes. This indicates that the entire operation is performed in the main thread. In fact, it is always hugeDataProcessorOp
, which runs in the main thread. I assume this is because it is an operation that always gets its answer last from the server.