Re: asynchronous nsurlconnection in nsoperation
Re: asynchronous nsurlconnection in nsoperation
- Subject: Re: asynchronous nsurlconnection in nsoperation
- From: Ariel Feinerman <email@hidden>
- Date: Fri, 23 Mar 2012 13:07:04 +0300
Andreas,
thank you for your answer! What is the well? I believe the streaming work
so we unarchive the this on the fly then write to url to avoid unnecessary
actions with copy
*
*
On Thu, Mar 22, 2012 at 4:10 PM, Andreas Grosam <email@hidden>wrote:
>
> On Mar 21, 2012, at 4:59 PM, Ariel Feinerman wrote:
>
> > Hi,
>
> > I wish to insert an asynchronous NSURLConnection into non-concurrent
> > NSOperation
> > the reason is to allow necessarily unarchive actions on the streaming
> date
>
> > Is this for iOS or Mac OS?
> > This for iOS
> >
> > The date is very large their amount is 400 Mb so the asynchronous is
> > necessarily
> > one cannot be in memory at once
> >
> > - (void) connection: (NSURLConnection *) connection didReceiveData:
> > (NSData*) data {
> > // Append the new data to receivedData.
> > [_receivedData appendData: data];
> > if ([_receivedData length] >= MAX_CHUNK) {
> > // unarchive
> > // write to file
> > // [receivedData setLength: 0];
> > }
> >
> > }
> >
> > Is there a correct way to do ?
>
> Yes.
>
> And your described approach works fine - if your connection delegates run
> on a secondary thread. Not sure, if you are actually required to use
> NSOperation, but basically, when you start your connection on a secondary
> thread (in order to keep the main thread responsive), it should work fine.
> (See the code below, how one can accomplish to start a connection on a
> secondary thread.)
>
> Though, your approach is not optimal and it also **requires** that you can
> actually process (unarchive) **partial** data.
>
>
> If you cannot process partial data, you have to search for a more
> elaborated solution to this problem:
>
> There are several approaches, from simple to more complex. And, there are
> approaches which look promising but won't work!
>
> What you need to use in any case is an **asynchronous** (NSURL)connection.
> Don't use a synchronously scheduled NSURLConnection with that amount of
> data!
>
> 1) The simplest one that works is to immediately write the received data
> to a temporary file (appending partial data to a file works fine). Then
> when finished downloading, process the temporary file, possibly leveraging
> mmap, NSStream, GCD or NSOperation. For this approach, you don't need
> anything special in the NSURLConnection. Starting it from the main thread
> is sufficient. Writing to the temp file is usually fast enough to keep the
> main thread responsive.
> When you are processing the data, you can schedule the task on a secondary
> thread.
>
> This approach is simple to implement, but is suboptimal performance wise -
> especially on devices with more than one CPU.
>
>
> 2) An approach that combines high performance with low memory foot-print
> is one that is a bit more elaborated. This would also require to schedule
> the connection on a secondary thread. (example on request)
>
>
> 3) A few approaches that WONT work and will likely eventually crash are
> the following:
>
> 3.a)
> - (void) connection:(NSURLConnection*)connection didReceiveData:(NSData*)
> data
> {
> dispatch_async(queue, ^{processData:data;}];
> }
> where processData: is supposed to be able to handle partial data.
> This will likely crash due to memory running out: If downloading is fast,
> and processing is slow, GCD queues a lot of buffers - until memory runs out.
>
> 3.b)
> - (void) connection:(NSURLConnection*)connection didReceiveData:(NSData*)
> data
> {
> [_receivedData appendData: data];
> }
> And when finished, processing _receivedData.
> This will crash due to memory running out.
>
>
>
>
>
> Regards
>
> Andreas
>
>
>
> ======================================================
>
> You can start a connection in a secondary thread, as follows:
>
> Anywhere, for instance a ViewController handling the "Start" Button:
>
> // start the NSURLConnection in a secondary thread:
> [NSThread detachNewThreadSelector:@selector
> (startConnectionInSecondaryThread)
> toTarget:self withObject:nil];
>
>
> And -startConnectionInSecondaryThread is implemented:
>
> static NSString* kDownloadConnectionRunLoopMode =
> @"MyViewControllerDownloadConnectionRunMode";
>
>
Won't this thrash the work?
- (void) startConnectionInSecondaryThread
> {
> NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
> [self startConnection];
> runLoopDone_ = NO;
> // Enable the run loop:
> // first, add a dummy source in order to prevent the Run loop from
> exiting
> // immediately when the connection closes :
> [[NSRunLoop currentRunLoop] addPort:[NSMachPort port]
> forMode:kDownloadConnectionRunLoopMode];
> do {
> BOOL processedSource = [[NSRunLoop currentRunLoop]
> runMode:kDownloadConnectionRunLoopMode beforeDate:[NSDate distantFuture]];
> } while (!runLoopDone_);
>
> [pool release];
> }
>
>
> And finally, -startConnection, which is shown in more detail, to show some
> things you need to care about:
>
> - (void) startConnection
> {
> // Note: startConnection can be performed on secondary threads, thus we
> need
> // to schedule UIKit methods onto the main thread.
>
> NSString* urlString = @"http://exmample.com";
> NSURL* url = [NSURL URLWithString:urlString];
>
> // Possibly configure/clear URL cache
>
> NSTimeInterval request_timeout = 60.0;
> NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url
> cachePolicy:0 timeoutInterval:request_timeout];
>
> [request setValue:@"gzip,deflate" forHTTPHeaderField:@
> "Accept-Encoding"];
> NSLog(@"Request header: %@", [request allHTTPHeaderFields]);
>
> // Create the URL connection and set its delegate:
> NSURLConnection* tmp = [[NSURLConnection alloc] initWithRequest:request
> delegate:self
> startImmediately:NO];
> self.connection = tmp;
> [tmp release];
>
> if (connection_ == nil) {
> dispatch_async(dispatch_get_main_queue(), ^{
> [self
> handleError:makeError(@"NSURLConnectionDownloadViewController", 3,
> @"Failure to create URL connection.")];
> self.messageLabel.text = @"creating connection failed";
> self.startDownloadButton.enabled = NO;
> });
>
> return;
> }
>
> NSLog(@"Start downloading %@", urlString);
> dispatch_async(dispatch_get_main_queue(), ^{
> self.messageLabel.text = @"start connection request";
> // Start the status bar network activity indicator. We'll turn it
> off when
> // the connection finishes or experiences an error.
> [UIApplication sharedApplication].networkActivityIndicatorVisible =
> YES;
> self.startDownloadButton.enabled = NO;
> self.cancelButton.enabled = YES;
> });
>
>
> // Schedule the connection's delegate methods in the current thread's
> run loop:
> [connection_ scheduleInRunLoop: [NSRunLoop currentRunLoop] forMode:
> kDownloadConnectionRunLoopMode];
>
> // Start the download
> [self.connection start];
> }
>
>
> ====================================================
>
>
> _______________________________________________
>
> Cocoa-dev mailing list (email@hidden)
>
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
>
--
best regards
Ariel
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden