-
-
Save zoul/578636 to your computer and use it in GitHub Desktop.
@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 |
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).
One minor issue I'd like to note:
@property(readonly) BOOL downloadOK;
This code is not used and throws a compiler warning.
You’re right, thanks. That was a leftover from the original code.
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.
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.
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!