Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>Standard opening comment, given prominence with an eye to the future: Apple's contributions to the LLVM project suggest that iOS 5 will come with self-zeroing weak references. So objects can hold references to other objects without owning them, and when those objects are deallocated all the pointers to them are magically set to <code>nil</code>. We'll have to wait for the public release of the developer tools to find out whether iOS implements that functionality (it needs some runtime support for obvious reasons) and, if so, whether Apple are using it for delegates. However, that's likely to be quite soon and as a developer you should be able to get the NDA version of the tools that is currently available, so it may be worth considering that route to a solution depending on the other practicalities of your project.</p> <p>More helpful for the here and now:</p> <p>The delegate for <code>NSURLConnection</code>s is immutable. Assuming the <code>NSURLConnection</code>s are created by tasks on the <code>NSOperationQueue</code> that you mention, you may also be creating them on some runloop other than that attached to the main thread, bringing a whole sweep of thread safety issues into the mix.</p> <p>I'd suggest that the smart thing to do is:</p> <ol> <li>ensure you're attaching all <code>NSURLConnection</code>s to the main runloop. To do that you should create connections so that they don't start immediately, use <code>scheduleInRunLoop:forMode:</code> to nominate <code>[NSRunLoop mainRunLoop]</code> (and probably <code>NSDefaultRunLoopMode</code>), then start them.</li> <li>simultaneously, keep a list of all connections that you have started. You'll probably want to use an <code>@synchronized</code> block to add then to a suitable <code>NSMutableArray</code></li> <li>when the object that is nominated to receive delegate methods is to be deallocated, perform <code>waitUntilAllOperationsAreFinished</code> on the operation queue and then send <code>cancel</code> to all ongoing connections (e.g. <code>[self.arrayOfConnections makeObjectsPerformSelector:@selector(cancel)]</code>)</li> </ol> <p>Connections are guaranteed not to communicate with their delegates after they have received the <code>cancel</code> message. You want to wait until all operations in the queue are finished to avoid a potential race condition whereby the view controller is deallocated having successfully killed all connections at some relevant time but a not-yet-completed operation then tries to add a new one.</p> <hr> <p>Additional, based on our comment discussion on the question:</p> <p>NSURLConnection has a built-in system to run asynchronously on a run loop. Run loops are Apple's version of an event loop, being in rough terms a list of messages to post. So they let you use a single thread to do lots of things but only if no single thing blocks the thread.</p> <p>Apple actually recommend that the most efficient way (in terms of processing and battery life) to do an asynchronous URL access is by allowing NSURLConnection to run asynchronously on the main thread. You might adjust your current <code>-(void) startConnection</code> to:</p> <pre><code>- (BOOL)startRequest { // we're going to do all this right here on the main thread, so there's // no need to package 'request' and 'keyInfo' into a dictionary and create // an operation // create the connection NNSURLConnection* connection = [[NNSURLConnection alloc] initWithRequest:request delegate:self]; if(connection) { NSMutableData *requestData = [[NSMutableData alloc] init]; connection.url = [keyInfo URL]; connection.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:keyInfo, kUserInfoKey, requestData, kRequestDataKey, nil]; [connectionsArray addObject:connection]; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; } // We used initWithRequest:delegate: so the request has already started. // The connection will now run asynchronously and we can wait for // delegate messages, which will be delivered via the runloop on this thread } </code></pre> <p>Then put the other things you were doing after the connection has ended into your <code>connectionDidFinishLoading:</code>:</p> <pre><code>- (void)connectionDidFinishLoading:(NNSURLConnection *)connection { if([connectionsArray indexOfObject:connection] != NSNotFound) { [connection cancel]; if([delegate conformsToProtocol:@protocol(ManagerDelegate)] &amp;&amp; [delegate respondsToSelector:@selector(managerFailed:withKey:errorCode:)]) { [delegate managerFailed:self withKey:[connection.userInfo objectForKey:kUserInfoKey] errorCode:ManagerErrorCodeTimeout]; if([connectionsArray count] &lt; 1) [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; } [connectionsArray removeObject:connection]; } /* ...and all the other stuff here... */ } </code></pre> <p>I've assumed throughout that <code>NNSURLConnection</code> is a subclass of <code>NSURLConnection</code> that just adds a few extra properties, not otherwise affecting behaviour.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload