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";

- (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 (Cocoa-dev@lists.apple.com)

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:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to