Reputation: 7310
I have a lot of connections going when my app starts, so I wanna put them on background threads so I can make new connections other than the starting connections before they all complete.
Below, threadedRequest:
is a method that's starting a NSURLConnection, but when I call performSelectorInBackground:withObject:
in the if
clause, the connection starts, but never finishes. The else
clause works fine and returns data from the connection
if (background)
{
[self performSelectorInBackground: @selector(threadedRequest:) withObject: args];
}
else
{
[self performSelector: @selector(threadedRequest:) onThread: [NSThread mainThread] withObject: args waitUntilDone: NO];
}
Upvotes: 1
Views: 226
Reputation: 438212
If you want to perform NSURLConnection
in the background, you must be sensitive to special conditions that apply to NSURLConnectionDataDelegate
methods in background threads. You have several options:
Use one of the non-delegate alternatives. Given that you're performing this in the background, you could use sendSynchronousRequest
, or if just requesting data from a simple URL, you could use NSData
class method dataWithURL
.
If you really need the delegate version (because you need progress updates via didReceiveData
or because you need one of the NSURLConnectDelegate
methods for authentication or the like, you have a couple of basic options:
You can create a NSOperationQueue
and set the delegate queue for the connection. For example, rather than:
- (void)startConnection:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection connectionWithRequest:request delegate:self];
}
You could do:
- (void)startConnection:(NSURL *)url
{
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[connection setDelegateQueue:self.connectionDelegateQueue];
[connection start];
}
Alternatively, you could do what AFNetworking does (see AFURLConnectionOperation.m
, for sample implementation):
Create a dedicated NSThread
for the NSURLConnectionDataDelegate
calls;
Start a NSRunLoop
on that thread;
Use the same startImmediately:NO
rendition of the NSURLConnection
init method as above; and
Use the scheduleInRunLoop
option for the NSURLConnection
before you start
it.
Or, easiest, you could just use AFNetworking
Two observations:
I might be inclined to use operation or dispatch queues instead of performSelectorInBackground
. See Concurrency Programming Guide.
By the way, you should be aware that there is a limit as to how many concurrent NSURLConnection
requests iOS can perform simultaneously (it's 5 or 6, I believe). Thus, if you initiate a dozen background requests and then initiate a "foreground" request, the "foreground" request may not start immediately. You may want to limit how many background requests that can operate concurrently if you want to avoid this potential problem.
Personally, when I want to constrain the number of network requests, I add my background requests to a NSOperationQueue
and set the number of maximum concurrent operations (e.g. I generally use 4), enjoying the benefits of concurrent operations, while not trying to perform more simultaneous connections than iOS will permit. By the way, when availing yourself of operation queue's maxConcurrentOperations
feature, you have to make sure that the requests are synchronous (or wrap the asynchronous connection in a custom NSOperation
that won't terminate until the connection is done or fails).
Upvotes: 1