What is the best way to run code on a separate thread?
NSInvocationOperation *operationToComplete = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(operationMethod) object:nil];
NSOperationQueue *queueToStart=[[NSOperationQueue alloc] init];
[queueToStart addOperation:operationToComplete];
-(void) operationMethod
{
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&_response error:&_error];
}
OR:
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request
delegate:self];
I've been doing the second way but am little bit confuse regarding this or use another way to do this.
Using operation queues with synchronous requests can be especially useful if you have many concurrent requests that you want to invoke. Operation queues make it easy to not only specify concurrent operation, but also to specify maxConcurrentOperationCount to constrain how many concurrent operations you want to run (which is important if you're firing off a whole bunch of them as you're limited to how many can operate concurrently with a given server).
Your latter example, with initWithRequest, is useful if you need the NSURLConnectionDataDelegate methods (for any of a number of reasons, e.g. you want to update progress view, you want to perform some streaming operations, you want to be able to cancel connection, etc.).
A third approach is to marry the initWithRequest approach with an NSOperationQueue, namely to wrap the NSURLConnection in its own NSOperation. This marries these two techniques, providing the richness of NSURLConnectionDataDelegate methods (cancelable, progress updates, etc.), with the power of operation queues (the ability to add network requests to queue for which you can configure degree of concurrency, establish dependencies between operations, etc.), but providing a nice interface. There are some idiosyncrasies to properly implement this approach (you have to schedule/create a runloop for NSURLConnection if you use it in an NSOperationQueue). I might, therefore, advise using a third party library, such as AFNetworking, if you want to enjoy the richness of this technique without getting lost in the implementation details.
In answer to your question of "which is best", it just depends upon what you're trying to do, as they all have pros and cons. But I prefer a NSOperation-based NSURLConnection, and would generally advise using AFNetworking if you want to simplify your development effort.
By the way, you can probably simplify the first example:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&_response error:&_error];
}];
Using Grand Central Dispatch (GCD) you can run code in background, which one is easier also.
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
});
And also NSURLConnection supports two modes of operation: asynchronous and synchronous. Neither uses separate threads at all.
Related
I have a task which is reading from a disk, potentially going to take quite some time, so don't want to do it in a main thread.. and what I want is to call a function X after reading from the disk. What is the best way to do this in iOS?
So far this is what I've tried:
NSInvocationOperation *processDataOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(readDisk:) object:nil];
[processDataOperation setQueuePriority:NSOperationQueuePriorityVeryHigh];
[processDataOperation setCompletionBlock:^(void){
NSMutableArray *feedItemsArray = [self generateFeedItemsFromDictionary:streamDiskData];
[self postFetchCompletedNotificationForDict:queryStringDict withFeedItems:feedItemsArray isFresh:NO];
}];
basically I am using NSInvocationOperation and then set it's completion block, however the issue is that in my completion block I need the result that is generated in readDisk. How do I access that in the completion block? It's nearly imposible right?
Using NSInvocations it is possible, but far more complicated than necessary, to achieve a trivial amount of work beyond the main thread.
Both GCD and NSOperations can be used to implement a wide array of concurrency strategies. From an object-oriented perspective, NSOperations are more highly abstracted than CGD blocks, which makes them (imo) easier to "design" with, and potentially optimized beyond the scope of where I'm implementing them. GCD is lower-level: This makes interacting with it appear slightly more complicated (it really isn't), but people who are in to that sorta stuff will tell you that it is "more efficient" and carries "less overhead".
My personal approach is to use NSOperations in scenarios where I have a designed/orchestrated concurrency pattern in my application, and use GCD for trivial concurrent/background operations.
If all I need to do is fire some arbitrary task that is not relevant to the design but needs to be done in the background, I'd use CGD. That's what I'd probably use in this case:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self readDisk];
NSMutableArray *feedItemsArray = [weakSelf generateFeedItemsFromDictionary:streamDiskData];
dispatch_sync(dispatch_get_main_queue(), ^{
//Call back to the main thread before performing/posting anything touching UIKit
[self postFetchCompletedNotificationForDict:queryStringDict withFeedItems:feedItemsArray isFresh:NO];
})
})];
You could always use grand central dispatch to do your operation in the background instead.
Since it is a block you can just call the method normally and store the result. Then grab the main queue if you need to update any UI or do whatever you need to after completion.
dispatch_queue_t queue = dispatch_queue_create("read disc", NULL);
dispatch_async(queue, ^{
result = [self readDisc];
dispatch_async(dispatch_get_main_queue(), ^{
//update UI or do whatever you need to do with the result of readDisc
});
});
dispatch_release(queue);
I know that the pattern in iPhone is to use ASync connection calls (using the informal protocols that is implemented by the current class).
In my case, I've created a utility class to do the networking staff and then return the data to the ViewController.
I find is inadequate to implement the connection model as Async in a utility class because r I will write a block of code in the ViewControlle such following: (which IMHO is bad)
MyUtilityConnection* utilConn = ....
while (true)
{
if ([utilConn checkUnderlyingAsyncConnectionFinishedLoading]) break;
}
NSData* dataFromUrl = [utilConn dataFromUnderlayingConn];
So, the question is, Does using Sync connection model in iPhone could causes problem? and solutions?
(What about the drawing will stril hanging until the data come???)
AVOID by all means to do synchronous connections! This will obviously freeze your UI (and it gets worse if you don't have a good bandwidth of course).
What you could do is to use the blocks syntax to write more readable code when you need to download data. Create a class that implements the NSURLConnection delegate methods, and then call the block when the data is done.
See my OHURLLoader class on github for example that does exactly that (and that's only one solution).
Usage example:
NSURL* url = ...
NSURLRequest* req = [NSURLRequest requestWithURL:url];
OHURLLoader* loader = [OHURLLoader URLLoaderWithRequest:req];
[loader startRequestWithCompletion:^(NSData* receivedData, NSInteger httpStatusCode) {
NSLog(#"Download of %# done (statusCode:%d)",url,statusCode);
outputTextView.text = loader.receivedString;
} errorHandler:^(NSError *error) {
NSLog(#"Error while downloading %#: %#",url,error);
outputTextView.text = [error localizedDescription];
}];
During sync methods (sendSynchronousRequest:returningResponse:error:) the UI is non-responsive (assuming that the sync method is called on the main thread).
But they are fine on background threads, the easiest way to accomplish sync calls on a background thread is with GCD.
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 [self parseXMLFileAtURL:path] method. is there anyway of stopping it midway? like terminating the method.
Reason for doing is because im running an apache http server on one pc and if the server is not running, the app will 'hang' if this method is called. so i want to to something like terminating the method after a certain amount of seconds , 5s maybe. and display an alert message.
I'd have two suggestions... one, if you possibly can, use NSMutableURLRequest with NSURLConnection to retrieve the data; which gives you much better control over things like timeout.
NSError * error;
NSURLResponse * response;
NSMutableURLRequest * request = [NSMutableURLRequest
requestWithURL:[NSURL URLWithString:#"http://..."]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:60.0];
// Not sure if you need this, but I frequently do POSTs as well, so whatever:
[request setHTTPMethod:#"GET"];
NSData * responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString * xml = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
(NB: You'll have to check the error response, I just omitted it for clarity)
Also, ideally (since in my example I use the synchronous method) this should be run on a background thread... but I found it much easier to run this on the background thread manually using "performSelectorInBackground:" and use the synchronous methods, than I did using the async methods. Keep in mind, you'll have to create your own auto release pool if you do that... but that's two lines, and it's super easy.
Short of that, it IS possible to terminate the method... you'd have to run it on a different thread, and kill the thread if it took too long... but really, the NSMutableURLRequest isn't so bad, and it already gives you the timeout options you're looking for.
The thread programming guide at: http://developer.apple.com/iphone/library/documentation/cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW2 talks about killing threads... and tells you (indirectly) how to do it... but if you simply kill the thread, you are almost guaranteed to leak something.
I'm pretty sure that you could try installing a signal handler with sigaction to handle SIGALRM and use the alarm function. There is, however, probably a better solution using the Cocoa framework. I'll leave this here, but it's probably not the easiest way.
A method is just a C(++) function, so there really no way to stop it.
In my iPhone app I want to download multiple files which are on IIS with authentication. On a button click i want to start the downloading process.
I know how to download a file with authentication.
NSURLRequest* request =
[NSURLRequest requestWithURL:mMovieURL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
movieConnection =
[[NSURLConnection alloc] initWithRequest:request delegate:self ];
and i have couple of delegate methods with the above code.
But how to do it with mutliple downlaods going at the same time.
Thanks,
I'm not familiar with MultipleDownload, but in case it doesn't meet your needs, the issue I take it is that you have a single object that is the delegate to many NSURLConnections, and you want to know how to keep them straight.
The delegate methods all return the NSURLConnection itself as their first parameter. So you can keep track of which data goes where by testing which NSURLConnection is calling you back. One way to do this is with an NSDictionary that maps the connection to its NSMutableData object. Now the trick is that you can't make an NSURLConnection be the key in a dictionary because it doesn't conform to NSCopying (and you wouldn't want it to). One way to work around this is to use the address of the connection such as:
NSString *key = [NSString stringWithFormat:#"%p", connection];
This will return a unique key for any object (the hex representation of its address). Some people use description for this purpose, but I don't like that because it's not a well defined interface. There's no promise that it be unique. In systems where I do this a lot, I implement the above -stringWithFormat: in a method called -uniqueIdentifier and make it a category on NSObject so anything can be tracked in a dictionary.
I often find it's easier just to create a small wrapper object so that each object controls its own NSURLConnection, much as I'm sure MultipleDownload is doing, but still this technique is useful in a variety of cases, whether you're managing multiple tableviews, or anything else that has a delegate.
EDIT: Replaced %x I had above with %p as noted by Peter. He's right, and I wasn't thinking correctly. Double-checking my code, I actually have been using %p, having run into this error before....
I've done this before when I wanted to download 10 XML files at the same time (it was much faster than queuing them to download one after the other). I used the libraries found here:
http://github.com/leonho/iphone-libs/tree/master
They were easy to implement and there's some example code on the front page to get you started.
self.urls = [NSMutableArray arrayWithObjects:
#"http://maps.google.com/maps/geo?output=json&q=Lai+Chi+Kok,Hong+Kong",
#"http://maps.google.com/maps/geo?output=json&q=Central,Hong+Kong",
#"http://maps.google.com/maps/geo?output=json&q=Wan+Chai,Hong+Kong",
nil];
self.downloads = [[MultipleDownload alloc] initWithUrls: urls];
self.downloads.delegate = self;
Good luck.
I think the simplest way to do this is to use NSOperation - and a NSOperationQueue.
This will mean that you can specifiy if each operation should happen sequentially or in parallel. You can even limit the number of parallel operations - so that there are a max of 5 (say) running at one time time and then other operations queue up behind.
This really is a great way of letting the OS handle multiple activities - and works well with the lazy loading type philosophy of the iPhone OS.
You can then get each operation to make a call back as it finishes - or even make progress callbacks on the main thread.
I have changed my code to all work this way now and found it to be much more robust and user firendly.
I'm not familiar with
MultipleDownload, but in case it
doesn't meet your needs, the issue I
take it is that you have a single
object that is the delegate to many
NSURLConnections, and you want to know
how to keep them straight.
The delegate methods all return the
NSURLConnection itself as their first
parameter. So you can keep track of
which data goes where by testing which
NSURLConnection is calling you back.
One way to do this is with an
NSDictionary that maps the connection
to its NSMutableData object. Now the
trick is that you can't make an
NSURLConnection be the key in a
dictionary because it doesn't conform
to NSCopying (and you wouldn't want it
to). One way to work around this is to
use the address of the connection such
as:
NSString *key = [NSString
stringWithFormat:#"%p", connection];
A better way would be to use NSValue with the valueWithNonretainedObject constructor. That way you can access the key object from the NSDictionary if you have to.
NSURLConnection is asynchronous and init exits immediately. Just run it multiple times.
NSArray *connections = [[NSArray alloc] initWithObjects:
[[NSURLConnection alloc] initWithRequest:request1 delegate:self ],
[[NSURLConnection alloc] initWithRequest:request2 delegate:self ],
[[NSURLConnection alloc] initWithRequest:request3 delegate:self ],
[[NSURLConnection alloc] initWithRequest:request4 delegate:self ],
nil];