Skip to content

Instantly share code, notes, and snippets.

@zoul
Created September 14, 2010 06:36
Show Gist options
  • Star 20 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save zoul/578636 to your computer and use it in GitHub Desktop.
Save zoul/578636 to your computer and use it in GitHub Desktop.
Asynchronous NSURLConnection in concurrent NSOperation
@interface DownloadOperation : NSOperation
{
NSURLRequest *request;
NSURLConnection *connection;
NSMutableData *receivedData;
}
@property(readonly) BOOL isExecuting;
@property(readonly) BOOL isFinished;
@property(readonly) NSData *receivedData;
- (id) initWithRequest: (NSURLRequest*) rq;
@end
#import "DownloadOperation.h"
@interface DownloadOperation ()
@property(assign) BOOL isExecuting;
@property(assign) BOOL isFinished;
@end
@implementation DownloadOperation
@synthesize isExecuting, isFinished, receivedData;
#pragma mark Initialization
- (id) initWithRequest: (NSURLRequest*) rq
{
[super init];
request = [rq retain];
return self;
}
- (void) dealloc
{
[request release];
[super dealloc];
}
#pragma mark NSOperation Stuff
- (void) start
{
NSParameterAssert(request);
// Bail out early if cancelled.
if ([self isCancelled]) {
[self setIsFinished:YES];
[self setIsExecuting:NO];
return;
}
[self setIsExecuting:YES];
[self setIsFinished:NO];
receivedData = [[NSMutableData alloc] init];
// Make sure the connection runs in the main run loop.
connection = [[NSURLConnection alloc] initWithRequest:request
delegate:self startImmediately:NO];
[connection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[connection start];
}
- (BOOL) isConcurrent
{
return YES;
}
#pragma mark NSURLConnection Callbacks
- (void) connection: (NSURLConnection*) cn didReceiveData: (NSData*) data
{
// Not cancelled, receive data.
if (![self isCancelled]) {
[receivedData appendData:data];
return;
}
// Cancelled, tear down connection.
[self setIsExecuting:NO];
[self setIsFinished:YES];
[connection cancel];
[connection release];
[receivedData release];
}
- (void) connectionDidFinishLoading: (NSURLConnection*) cn
{
[self setIsExecuting:NO];
[self setIsFinished:YES];
[receivedData release];
[connection release];
}
- (void) connection: (NSURLConnection*) cn didFailWithError: (NSError*) error
{
[self setIsExecuting:NO];
[self setIsFinished:YES];
[connection release];
[receivedData release];
}
// http://stackoverflow.com/questions/3573236
+ (BOOL) automaticallyNotifiesObserversForKey: (NSString*) key
{
return YES;
}
@end
@jocull
Copy link

jocull commented Nov 6, 2010

Alright, after hours of painful debugging, here is something to take important note of. Notice this line:

connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];

When you run this line, you are setting the object as its own delegate for the connection it owns. This increases its retain count by one, and to get the object to deallocate you'll actually have to release it twice! In my copy of this source, I've altered it to this:

connection = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:NO];
[self release]; //We do not want the delegate to increase our retain count!

Other than that issue, the source is very good. Thank you!

@zoul
Copy link
Author

zoul commented Nov 8, 2010

Oh, thank you. The delegate is not a property on NSURLConnection, which is probably why it’s so easy to miss the fact that it’s retained and not assigned (as delegates usually are).

@barchard
Copy link

barchard commented Aug 5, 2011

One minor issue I'd like to note:

@property(readonly) BOOL downloadOK;

This code is not used and throws a compiler warning.

@zoul
Copy link
Author

zoul commented Aug 6, 2011

You’re right, thanks. That was a leftover from the original code.

@zoul
Copy link
Author

zoul commented Mar 15, 2012 via email

@tciuro
Copy link

tciuro commented May 24, 2012

Hm. I'm not sure you really need to release the delegate. According to the NSURLConnection documentation:

Special Considerations
The connection retains delegate. It releases delegate when the connection finishes loading, fails, or is canceled.

@flexaddicted
Copy link

Dear zoul,

Thanks for sharing this.

Is there any advantage to run NSURLConnection within a concurrent NSOperation? I suppose it is possible to have the same effect just creating a wrapper around the connection.

My supposition is that I could use queues in order to manage the number of running operations, dependencies among operations and so on.

I ask it since the connection is shunted on the main thread and, so, callbacks will be called on that thread. So, no threading mechanism is involved here.

Thank you for your attention.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment