I'm a bit confused about GCD
I try caling a class where my webservice call is executed:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[searchService doSearch:self.searchData];
});
I'm using Sudzc-generated webservice. The actual service call to the service generated with Sudzc and is inside the SearchService class is the following:
[service doSearch:self action:#selector(doSearchHandler:) e: searchArgs];
Once the call is done I should return to:
- (void) doSearchHandler: (id) value {
}
Without using the GCD it works fine, but when I add it, I never return to the doSearchHandler method.
I had the problem of the Sudzc operation blocking the main thread.
However, the Soap request is not the problem as, although the NSURLRequest is executed on the main thread, its data is managed by the delegate didReceiveData
What was blocking my UI was the processing of the received XML that happened in the Handler method.
In the handler method (- (void) SudzcRequestHandler: (id) value { ...) I process the data like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
...processing...
[[NSNotificationCenter defaultCenter] postNotificationName:KManagerHasFinishedLoading object:self userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:NOTIFICATION_RESULT_OK], #"Result", nil]];
});
I run the processing in Manager, which is a singleton object, but other implementations may vary. So the notification is used to let my ViewController know that processing has ended.
You should look into the Sync-Async pattern. The two links from the answer of this SO will help. It doesn't use GCD directly. But you can adapt easily.
Related
My problem is as follows,
I have 4 class as follows,
A) UIviewController (UI)
B) Static Method web service request class
c) Static Method web service response parsing class
D) Static Method web service response processing class
Flow of data is class A->B->C->D and from D class to again A ,
* required thing is B,C,D are in background process
Please let me know how to write source code to resolve this problem?
Please fill free to give any other alternative method solution for this problem.
Thanks in Advance.
You can use NSNotificationCenter service for callback. When you are done with process in Class D send notification. In Class A add/remove a observer
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[[NSNotificationCenter defaultCenter] addObserverForName:#"SyncCompleted" object:nil queue:nil usingBlock:^(NSNotification *note) {
[self loadRecordsFromCoreData];// Do smoething
}];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"SyncCompleted" object:nil];
}
In Class D when the process completes call
[[NSNotificationCenter defaultCenter] postNotificationName:#"SyncCompleted"
object:nil];
It's very hard to understand what you're asking, but with iOS, when you're doing most web services-type work, I tend to just recommend using AFNetworking rather than rolling your own: https://github.com/AFNetworking/AFNetworking
It handles most of the things you'll generally care about, like downloading and doing delegate processing outside of the main NSOperationQueue, etc.
Alternatively, if you only care about iOS7, the new NSURLSession class also makes these things easy: https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/Introduction/Introduction.html
But you should rarely need to roll your own.
Since at least the network operations are inherently asynchronous, your problem can be viewed as a "common asynchronous pattern":
When you want to execute asynchronous operation B immediately after asynchronous operation A has been finished, this is called "continuation".
There are a few approaches how one can implement continuation. This is dependent on how the asynchronous operation signals "completion" or "error" to the call-site.
Suppose the asynchronous operation is wrapped into a method which has a parameter completion which is a block. In this case, the asynchronous method signals completion through calling the caller-provided completion block, and passing the result as a parameter to the block:
typedef void (^completion_t)(id result);
- (void) doSomethingAsync:(completion_t)completionHandler;
call site:
[self doSomethingAsync:^(id result){/* now, result is available */});
Note, that the completion handler will be provided by the call-site, and that the completion handler will be called by the asynchronous operation once the result has been evaluated.
Now, in order to invoke a second asynchronous operation which gets invoked immediately after the first has been completed you can simply invoke it in the completion handler of the first:
Suppose the second asynchronous method requires an input, which must be provided by the call-site in parameter input:
- (void) doSomethingElseWithParam:(id)input
completion:(completion_t)completionHandler;
Suppose the second asynchronous method takes the previously evaluated result as input. Here's the call site invoking asynchronous method doSomethingAsync:, and when completed it invokes doSomethingElseWithParam:completion:. Finally, when the second asynchronous method finished, the result will be printed to the console:
[self doSomethingAsync:^(id result){
[self doSomethingElseWithParam:result completion:^(id finalResult) {
NSLog(#"Final result: %#", finalResult);
}]
});
"Chaining" asynchronous methods in such a way is also called "continuation".
Of course, a real implementation would handle all the error checks.
There are other ways to implement this. This was an example with completion blocks. One disadvantage with this approach is, that it becomes difficult to impossible to implement cancellation.
Another approach is using NSOperation's dependency feature (see NSOperation's addDependency: method (NSOperation Class Reference). NSOperation objects can be easily cancelled.
And yet another approach utilizes a Promise (see wiki: Futures and Promises) which represents the eventual result of an asynchronous operation. (There are a few Objective-C third party libraries which implement a Promise).
I have a toolkit that I need to work with (to interface with a remote service). This toolkit queries the remote service and asks for results. It does this asynchronously, which in most cases is good, but not for creating concise methods. I want to make methods similar to the following:
-(NSArray *)getAllAccounts {
NSString *query = #"SELECT name FROM Account";
//Sets "result" to the query response if no errors.
//queryResult:error:context: is called when the data is received
[myToolkit query:query target:self selector:#selector(queryResult:error:context:) context:nil];
//Wait?
return result.records;
}
The problem is, inside the toolkit the methods call each other using #selector, not direct calls, so getting return values is difficult. Further, the actual query uses:
NSURLConnection *connection = [[[NSURLConnection alloc] initWithRequest:aRequest delegate:self] autorelease];
Which is asynchronous. By the time the data has been received from the service, my method has long ago returned... without the information. So my question is this: Is there a way to pause execution until the data has been returned? Could I accomplish this using a second thread to get the data while the main thread rests (or using 3 threads so the main thread doesn't rest?)
I don't want to edit the toolkit to change their method (or add a new one) to be synchronous, so is there a way to make a method as I want?
You might want to consider NOT making it all synchronous, especially if the sample code in your post is run on your main application thread. If you do that, the main thread will block the UI and the application will cease to respond until the remote transaction is complete.
Therefore, if you really insist on the synchronous approach, then you should definitely do it in a background thread so that the UI does not become unresponsive, which can actually lead to your App getting killed by the OS on iphone.
To do the work in a background thread, I would strongly recommend using the Grand Central Dispatch stuff, namely NSBlockOperation. It will free you from having to actually create and manage threads and makes your code pretty neat.
To do the synchronous thing, take a look at the NSCondition class documentation. You could do something like the following:
NSCondition* condition = ...;
bool finished = NO;
-(NSArray *)getAllAccounts {
[condition lock];
NSString *query = #"SELECT name FROM Account";
//Sets "result" to the query response if no errors.
//queryResult:error:context: is called when the data is received
[myToolkit query:query target:self selector:#selector(queryResult:error:context:) context:nil];
while (!finished)
[condition wait];
[condition unlock];
return result.records;
}
Then in the method called by the toolkit to provide the results you'd do:
- (void) queryResult:error:context: {
// Deal with results
[condition lock]
finished = YES;
[condition signal];
[condition unlock];
}
You'd probably want to encapsulate the "condition" and "finished" variables in your class declaration.
Hope this helps.
UPDATE: Here is some code to offload the work to a background thread:
NSOperationQueue* queue = [NSOperationQueue new];
[queue addOperationWithBlock:^{
// Invoke getAllAccounts method
}];
Of course, you can keep the queue around for later use and move the actual queuing of the work to inside your method call to make things neater.
The way to wait is to return from your current code. Finish up doing what you want done after the wait, in the asynchronous callback method you specify. What's so difficult about that?
Any synchronous waits in the main UI thread will block the UI and make the user think your app has locked up, which is likely far worse than your thinking the code isn't concise enough.
I have a library project that uses ASIHTTPRequest to make URL requests and parse the responses. The library will be used by a separate iPhone app project.
If my iPhone controller code responds to a touch event, then calls into the library to make URL requests, how do I best perform the requests asynchronously?
In the library, if I use the delegate pattern for asynchronous requests as shown in the ASIHTTPRequest sample code, how do I return data from the library back to the calling code in the iPhone controller?
If I instead make synchronous URL requests with ASIHTTPRequest inside the library, what's the easiest way to put the calls to the library from the iPhone controller on a separate thread to avoid tying up the UI thread?
I'm no ASIHTTPRequest expert (NSURLRequest has always done me fine), but from a quick poke at the code, it looks like you'd use its delegate and didFinishSelector properties to give it someone to tell when the URL request is finished. So, for example:
- (void)startURLRequest
{
ASIHTTPRequest *myRequest;
/* code to set the request up with your target URL, etc here */
myRequest.delegate = self;
myRequest.didFinishSelector = #selector(HTTPRequestDidFinish:);
/* ... */
[myRequest startAsynchronous];
}
- (void)HTTPRequestDidFinish:(ASIHTTPRequest *)request
{
NSLog(#"Request %# did finish, got data: %#", request, request.data);
[myTargetForData didReceiveData:request.data fromURL:request.originalURL];
}
Apple explicitly recommend that you use the built-in runloop style mechanisms for asynchronous HTTP fetching, not separate threads. Using separate threads is likely to result in worse performance — at least in terms of battery life and/or device heat, even if it's still fast enough.
That said, as a learning point, by far the quickest way to switch something onto a separate thread and have it report back to the main thread (remember: UIKit objects may be messaged only from the main thread) is by changing this:
- (void)postResult:(NSString *)result
{
instanceOfUILabel.text = result;
}
- (void)doExpensiveOperationOn:(NSString *)source
{
/* lots of expensive processing here, and then... */
[self postResult:result];
}
- (IBAction)userWantsOperationDone:(id)sender
{
[self doExpensiveOperationOn:#"some value or another"];
}
Into this:
- (void)postResult:(NSString *)result
{
instanceOfUILabel.text = result;
}
- (void)doExpensiveOperationOn:(NSString *)source
{
/* we're on a thread without an autorelease pool now, probably we'll want one */
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
/* lots of expensive processing here, and then... */
/* in this simplified example, we assume that ownership of 'result' is here on this thread, possibly on the autorelease pool, so wait until postResult has definitely finished before doing anything that might release result */
[self performSelectorOnMainThread:#selector(postResult:) withObject:result waitUntilDone:YES];
[pool release];
}
- (IBAction)userWantsOperationDone:(id)sender
{
[self performSelectorOnBackgroundThread:#selector(doExpensiveOperationOn:) withObject:#"some value or another"];
}
There's about a million possible concurrency errors you can make by just going threaded without thinking about it though, and in that example an obvious problem is that whatever triggered the IBAction can [probably] trigger it several more times before doExpensiveOperationOn has finished. Multithreading is not something to be dashed into lightly.
For anyone's future reference, the easiest approach I found is to use the async request functionality built into ASIHTTPRequest, setting my library object as the delegate and setting the didFinishSelector: and didFailSelector: values to different methods inside my library for each request.
At the end of processing each response, I assign the parsed response (an NSString* or NSArray*) to a property of my library object instead of returning a value.
When my iOS view controller delegate is loaded, I add a change observer to each of the properties in the library using Key-Value Observing. When the response is parsed and assigned to the property in the library, the observeValueForKeyPath:ofObject:change:context: method is called in the code of my view controller delegate, and from there I can figure out which property was changed and therefore what UI needs to be updated.
this is my first question here, so excuse me if I made any mistakes!
In my iPhone project I have a method running in a thread which takes a long time to execute (that's why it runs in a thread).
[NSThread detachNewThreadSelector:#selector(methodToBeCalledInAThread) toTarget:self withObject:nil];
// ...
-(void)methodToBeCalledInAThread {
MyClass *myClass = [[MyClass alloc] init];
[myClass setDelegate:self];
[myClass veryIntensiveComputing];
[myClass release];
}
My goal is to notifiy the ViewController calling this method of any progress going on in this method. This is why I set the ViewController as a delegate of the class.
Now in the expensive method I do the following:
if(self.delegate != nil) {
[self.delegate madeSomeProgress];
}
But unfortunately this does not work, because (I think) I'm in a background thread.
How do I achieve to notify the delegate of any changes with the method being executed asynchronously?
Try [self.delegate performSelectorOnMainThread: #selector(madeSomeProgress) withObject: nil waitUntilDone: YES];....
See the documentation for details.
This will synchronously perform the operation on the main thread. As long as nothing in the main thread tries to execute stuff in that secondary thread, this is mostly safe to do.
However if you don't want to block the computation until the main thread services the request, you can pass NO to not wait. If you do so, then you also have to worry about thread synchronization. The main thread may not immediately service the request and, when it does, your background thread may be in the process of mutating state.
Threads are hard.
How can I create a new file containing just it's own delegate so that I can make an ASIHTTPRequest with its own asynchronous ending, and something easy enough where I just need to set [request setDelegate:self]; to something like [request setDelegate:AlternateDelegate]; and just add an include at the begining of the document to reference the AlternateDelegate delegate
I know this question is old, but in case anyone comes across it:
#Hankweb seemes to be talking about using a request as its own delegate. There are certainly situations where this works. For example, I'm working on a project that uses ASIHTTPRequest to fetch JSON from a remote source and import it into a Core Data store.
This operation (literally, as ASIHTTPRequest is a subclass of NSOperation) is almost entirely self-contained; I have a custom request on a background thread using a streaming JSON parser to import objects into a NSManagedObjectContext, which, when saved, triggers a notification that I catch internally and pass to the main thread's context using performSelectorOnMainThread:waitUntilDone:.
I'm using ASIHTTPRequest's block support to accomplish this; in my custom initWithURL: method, I set up the relevant callbacks (dataReceivedBlock, completionBlock, failureBlock, etc.). The traditional delegation pattern (using the ASIHTTPRequestDelegate protocol) should also work, though.
One gotcha: you should make sure the request doesn't retain itself too many times, or else you'll end up with a memory leak. This can be easy to miss when using multiple threads, and especially when using blocks. Instead of:
- (id)initWithURL:(NSURL *aURL) {
//...
[self setCompletionBlock:^{
[self doSomething];
}];
//...
return self;
}
Use the __weak attribute (or __block if you're not using ARC) when referencing self within the blocks:
- (id)initWithURL:(NSURL *aURL) {
//...
__weak id blockSelf = self;
[self setCompletionBlock:^{
[blockSelf doSomething];
}];
//...
return self;
}
If you don't know why this is important, make sure to read Apple's guide to blocks in Objective-C, and the ASIHTTPRequest block API documentation.
A delegate for ASIHTTPRequest is just a standard objective C object. Just create a new class, include it's header, create/get the object and set the delegate to be that object.
Have you tried this and run into a problem? If so what is the problem?