Custom block inside dispatch_async - iphone

This code works
[[MyManager sharedManager] makeRequestAndParsingfor:someParameters
success:^(NSDictionary * dictionary){
// Sucessful response
NSLog(#"Success!!");
}
failure:^(NSError* error){
// Error response
NSLog(#"Failure!");
}];
But this whenever I run the same in the background it never enters in success or failure block.
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[[MyManager sharedManager] makeRequestAndParsingfor:someParameters
success:^(NSDictionary * dictionary){
// Sucessful response
NSLog(#"Success!!");
}
failure:^(NSError* error){
// Error response
NSLog(#"Failure!");
}];
}];
Can anybody explain me what happens? The method makeRequestAndParsingfor: makes an asynchronous request (with blocks again) and then parses the result. My debugger shows that it never gets in its own success/failure in the second case. In the first case it works like a charm. Any ideas?

Does your method 'makeRequestAndParsingfor' use block_copy() of the args, and store the return in a strong variable (to get the blocks in the heap)? Also add asserts() so you can verify you get both blocks in 'makeRequestAndParsingfor', and even retest before calling one or the other. [In the past use of block_copy() was necessary but now not 100% sure.]
Note that in the second case, where 'makeRequestAndParsingfor' runs in a concurrent queue that multiple of these can call the blocks concurrently - not sure what your success/failure block does, but it better be thread safe, or you should run the block on the main queue in 'makeRequestAndParsingfor' (which I do in my similarly constructed app).

Related

Objective-c/iOS: setting status text in async function is slow

In my app I'm doing some communication with a remote server and as this might be slow I thought it would be a good idea to run that code asynchronously. I have my communication code in a block that I pass to dispatch_async. This code does the communication and when it's done it sets the text of a label. This last part is the problem. The text is set, but it occurs after a delay of a few seconds. This is my code.
- (void)doNetworkingTask {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Slow network task goes here.
// Slow network task done, notify the user.
[self.myLabel setText:#"task done."];
NSLog(#"task done.");
});
}
What happens here is that my network task completes, the NSLog-text is logged and after a couple of seconds, the text of the label is updated. My question is 1) why does the label text not update instantly? and 2) what is a proper way of doing what I want to do? (do the slow network task without blocking anything else, update the user through a text label once I'm done.)
UI updates must be on the main thread. Update your code to something like this:
- (void)doNetworkingTask {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Slow network task goes here.
// Slow network task done, notify the user.
dispatch_async(dispatch_get_main_queue(), ^{
[self.myLabel setText:#"task done."];
});
NSLog(#"task done.");
});
}

How to prevent IOS concurrent NSOperation from running on main thread

I am writing an application that periodically fetches data from a web server using ASI HTTP and then processes that data to display something relevant to the user on the UI. The data is retrieved from different requests on a single server. The data itself needs to be processed in a specific order. One of the blocks of data is much bigger than the other ones.
In order not to lock the UI while the data is being processed, I have tried to use the NSOperationQueue to run the data processing on different threads. This works fine about 90% of the times. However, in the remaining 10% of the time, the biggest block of data is being processed on the main thread, which cause the UI to block for 1-2 seconds. The application contains two MKMapViews in different tabs. When both MKMapViews tabs are loaded the percentage of time the biggest block of data is being processed on the main thread increases above 50% (which seems to point to the assumption that this happens when there is more concurrent activity).
Is there a way to prevent the NSOperationQueue to run code on the main thread?
I have tried to play with the NSOperationQueue –setMaxConcurrentOperationCount:, increasing and decreasing it but there was no real change on the issue.
This is the code that starts the periodic refresh:
- (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 the ASI HTTP completion block that starts the data processing:
[_request setCompletionBlock:^{
[self.delegate setResponseString:_request.responseString];
[[MyModel queue] addOperation:operation]; // operation is a NSInvocationOperation that calls the delegate parse method
}];
I have added this block of code in all NSInvocationOperation Invoked method at the entry point:
if([NSThread isMainThread]){
NSLog(#"****************************Running <operation x> on Main thread");
}
The line is printed every time the UI freezes. This shows that the whole operation is run on the main thread. It is actually always the hugeDataProcessorOp that is run on the main thread. I assume that this is because it is the operation that always receives its answer last from the server.
After much investigation in my own code, I can confirm that this was a coding error.
There was an old call remaining that did not go through the NSInvocationOperation but was calling the selector that NSInvocationOperation should have called directly (therefore not using the concurrent NSOperationQueue.
This means that the NSOperationQueue DOES NOT use the main thread (except if it is the one retrieved by +mainQueue).
Override isConcurrent on your NSOperation and return YES. According to the documentation this will cause your NSOperation to be run asynchronously.

stop and restart a method

I have to stop and restart a simple method that takes from 1 to 2 seconds to execute. How can this be accomplished? I have already tried [NSObject cancelPreviousPerformRequestsWithTarget:self] but it works only with performselector after delay. I have also tried to create a new thread but it doesn't seem to work...
This is my method:
-(IBAction)MyMethod
{
NSLog(#"start");
//Here is the code that takes time to execute. It regards UI intervention,graphic calculation, x and y position etc.
NSLog(#"end");
}
I want this effect: one click on the linked UIButton and the method start (so print start log and end log). If I click the linked UIButton before the NSLog is printed, the method must stop. Is this possible?
You'll want to use a background task. I would suggest subclassing and using a NSOperation and checking for isCancelled within the body of main. See Apple's documentation on using NSOperation and NSOperationQueue.
Well, to do this with threads what I usually do is dividing things into 3 sections:
start.
processing.
finish.
And it looks like this:
-(void)start:(id)sender{
//prepare everything and anything
[NSThread detachNewThreadSelector:#selector(processing:) toTarget:self withObject:nil];
}
-(void)processing:(id)sender{
//Perform all your calculations, you can't modify UI elements here
[self performSelectorOnMainThread:#selector(finish:) withObject:nil waitUntilDone:NO];
}
-(void)finish:(id)sender{
//Wrap everything up and do any modifications to the UI
}
Now, to cancel this you could add maybe use:
Cancels perform requests previously registered with
performSelector:withObject:afterDelay:.
+ (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(id)anArgument

Asynchronous callback for network in Objective-C Iphone

I am working with network request - response in Objective-C. There is something with asynchronous model that I don't understand.
In summary, I have a view that will show my statuses from 2 social networks: Twitter and Facebook. When I clicked refresh, it will call a model manager. That model manager will call 2 service helpers to request for latest items. When 2 service helpers receive data, it will pass back to model manager and this model will add all data into a sorted array.
What I don't understand here is that : when response from social networks come back, how many threads will handle the response. From my understanding about multithreading and networking (in Java), there must have 2 threads handle 2 responses and those 2 threads will execute the code to add the responses to the array. So, it can have race condition and the program can go wrong right? Is it the correct working model of iphone objective-C? Or they do it in a different way that it will never have race condition and we don't have to care about locking, synchronize?
Here is my example code:
ModelManager.m
- (void)updateMyItems:(NSArray *)items {
self.helpers = [self authenticatedHelpersForAction:NCHelperActionGetMyItems];
for (id<NCHelper> helper in self.helpers) {
[helper updateMyItems:items]; // NETWORK request here
}
}
- (void)helper:(id <NCHelper>)helper didReturnItems:(NSArray *)items {
[self helperDidFinishGettingMyItems:items callback:#selector(model:didGetMyItems:)];
break;
}
}
// some private attributes
int *_currentSocialNetworkItemsCount = 0; // to count the number of items of a social network
- (void)helperDidFinishGettingMyItems:(NSArray *)items {
for (Item *item in items) {
_currentSocialNetworkItemsCount ++;
}
NSLog(#"count: %d", _currentSocialNetworkItemsCount);
_currentSocialNetworkItemsCount = 0;
}
I want to ask if there is a case that the method helperDidFinishGettingMyItems is called concurrently. That means, for example, faceboook returns 10 items, twitter returns 10 items, will the output of count will ever be larger than 10?
And if there is only one single thread, how can the thread finishes parsing 1 response and jump to the other response because, IMO, thread is only executed sequently, block of code by block of code
Yes, there is probably a thread per network request. The trick is to handle the response on the main thread. You should have something like this:
- (void)helper:(id <NCHelper>)helper didReturnItems:(NSArray *)items;
{
[self performSelectorOnMainThread:#selector(helperDidFinishGettingMyItems:)
withObject:items
waitUntilDone:NO];
}
Putting the response back onto the main thread will avoid a whole bunch of multithreading problems.
Also, the output of count will never be larger than 10. It's just that multiple threads may be running helperDidFinishGettingMyItems: at the same time. They won't automatically combine the two arrays.
The counter could possibly be more than 10, because multiple threads could be increasing that ivar at the same time.

concurrent background downloads on iphone

I am trying to create class that will handle multiple downloads at same time (I need to download a lot of small files) and I have problems with "disappearing" connections.
I have function addDonwload that adds url to list of urls to download, and checks if there is free download slot available. If there is one it starts download immediately. When one of downloads finishes, I pick first url form list and start new download.
I use NSURLConnection for downloading, here is some code
- (bool) TryDownload:(downloadInfo*)info
{
int index;
#synchronized(_asyncConnection)
{
index = [_asyncConnection indexOfObject:nullObject];
if(index != NSNotFound)
{
NSLog(#"downloading %# at index %i", info.url, index);
activeInfo[index] = info;
NSURLRequest *request = [NSURLRequest requestWithURL:info.url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:15];
[_asyncConnection replaceObjectAtIndex:index withObject:[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:TRUE]];
//[[_asyncConnection objectAtIndex:i] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
return true;
}
}
return false;
}
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
[self performSelectorOnMainThread:#selector(DownloadFinished:) withObject:connection waitUntilDone:false];
}
- (void)DownloadFinished:(id)connection
{
NSInteger index = NSNotFound;
#synchronized(_asyncConnection)
{
index = [_asyncConnection indexOfObject:(NSURLConnection*)connection];
}
[(id)activeInfo[index].delegate performSelectorInBackground:#selector(backgroundDownloadSucceededWithData:) withObject:_data[index]];
[_data[index] release];
[activeInfo[index].delegate release];
#synchronized(_asyncConnection)
{
[[_asyncConnection objectAtIndex:index] release];
[_asyncConnection replaceObjectAtIndex:index withObject:nullObject];
}
#synchronized(downloadQueue)
{
[downloadQueue removeObject:activeInfo[index]];
[self NextDownload];
}
}
- (void)NextDownload
{
NSLog(#"files remaining: %i", downloadQueue.count);
if(downloadQueue.count > 0)
{
if([self TryDownload:[downloadQueue objectAtIndex:0]])
{
[downloadQueue removeObjectAtIndex:0];
}
}
}
_asyncConnection is my array of download slots (NSURLConnections)
downloadQueue is list of urls to download
What happens is, at the beginning everything works ok, but after few downloads my connections start to disappear. Download starts but connection:didReceiveResponse: never gets called. There is one thing in output console that I don't understand I that might help a bit. Normaly there is something like
2010-01-24 21:44:17.504 appName[3057:207]
before my NSLog messages. I guess that number in square brackets is some kind of app:thread id? everything works ok while there is same number, but after some time, "NSLog(#"downloading %# at index %i", info.url, index);" messages starts having different that second number. And when that happens, I stop receiving any callbacks for that urlconnection.
This has been driving me nuts as I have strict deadlines and I can't find problem. I don't have many experiences with iphone dev and multithreaded apps. I have been trying different approaches so my code is kinda messy, but I hope you will see what I am trying to do here :)
btw is anyone of you know about existing class/lib I could use that would be helpful as well. I want parallel downloads with ability o dynamically add new files to download (so initializing downloader at the beginning with all urls is not helpful for me)
You've got a bunch of serious memory issues, and thread synchronization issues in this code.
Rather than go into them all, I'll ask the following question: You are doing this on a background thread of some kind? Why? IIRC NSURLConnection already does it's downloads on a background thread and calls your delegate on the thread that the NSURLConnection was created upon (e.g., your main thread ideally).
Suggest you step back, re-read NSURLConnection documentation and then remove your background threading code and all the complexity you've injected into this unnecessarily.
Further Suggestion: Instead of trying to maintain parallel positioning in two arrays (and some sketchy code in the above relating to that), make one array and have an object that contains both the NSURLConnection AND the object representing the result. Then you can just release the connection instance var when the connection is done. And the parent object (and thus the data) when you are done with the data.
I recommend that you take a look at this:
http://allseeing-i.com/ASIHTTPRequest/
It's a pretty sophisticated set of classes with liberal licensing terms (free too).
It may provide a lot of the functionality that you are wanting.
This snippet can be the source of the bug, you release the object pointed to by the activeInfo[index].delegate pointer right after issuing async method call on that object.
[(id)activeInfo[index].delegate performSelectorInBackground:#selector(backgroundDownloadSucceededWithData:) withObject:_data[index]];
[_data[index] release];
[activeInfo[index].delegate release];
Do you use connection:didFailWithError: ? There may be a timeout that prevents the successful download completion.
Try to get rid of the #synchronized blocks and see what happens.
The string inside the square brackets seems to be thread identifier as you guessed. So maybe you get locked in the #synchronized. Actually, I don't see a reason for switching thread - all the problematic code should run in the main thread (performSelectorOnMainThread)...
Anyhow, there is no need to use both the #synchronized and the performSelectorOnMainThread.
BTW, I didn't see the NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self]; line. Where do you initiate the connection?
As for the parallel downloads - I think that you can download more than one file in a time with the same code that you use here. Just create a separate connection for each download.
Consider just keeping a download queue along with a count of active connections, popping items off the top of the queue when downloads complete and a slot becomes free. You can then fire off NSURLConnection objects asynchronously and process events on the main thread.
If you find that your parallel approach prohibits doing all of the processing on the main thread, consider having intermediary manager objects between your main thread download code and NSURLConnection. Using that approach, you'd instantiate your manager and get it to use NSURLConnection synchronously on a background thread. That manager then completely deals with the downloading and passes the result back to its main thread delegate using a performSelectorOnMainThread:withObject: call. Each download is then just a case of creating a new manager object when you've a slot free and setting it going.