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).
Related
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.
can any one tell me what is different when I call method using performSelectorOnMainThread and calling same method without performSelector.
For Exa.
-(void)sampleCALL{
..........
}
NOW Call this method using these two senario:
[[self performSelectorOnMainThread:#selector(sampleCALL) withObject:nil waitUntilDone:NO];];
or
[self sampleCALL];
How these two method are getting executed?
Please help me to find this concept properly.
in firs one case [self sampleCALL]; your method will be called in the thread where control was at current time. it will maintain all the stack manipulation what it does for method calling from another method.
while
[[self performSelectorOnMainThread:#selector(sampleCALL) withObject:nil waitUntilDone:NO];];
calls the method in main thread whatever the controls current thread is. All UI actions are performed in main thread always.
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 an object (Processor) containing several methods that perform lengthy calculations. I'd like to use those methods both on the main thread and in NSOperation subclasses.
Inside my NSOperation subclass code I repeatedly call isCancelled, so cancellation is fairly responsive. However, when the operation calls one of those lengthy Processor methods, it isn't able to respond to cancellation until that method returns.
Is there a good way to write methods so they can be used both with and without operations? I'm considering adding an operation argument to my CPU-intensive Processor methods and writing them like this:
- (void)calculateWithOperation:(NSOperation *)operation {
do {
if (operation != nil && [operation isCancelled]) {
return;
}
// Do some more calculation...
} while (! finished);
}
// For convenient main thread execution.
- (void)calculate {
[self calculateWithOperation:nil];
}
Has anyone else run into this issue before? Is there a better way?
The only way for an NSOperation to respond to canceling is to check if it's isCancelled as frequently as feasible. After all, it's just a thread which needs to exit when a flag is set. Basically, the isCancelled infrastructure is needed to gracefully free the operation's resources. So I'd say you just have to pepper the expensive method with checks.
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.