NSFileHandle & Writing Asynch to a file in iOS

Cam picture Cam · Jan 13, 2012 · Viewed 7.3k times · Source

I have a situation that I receive a byte data through Web Services request and want to write it to a file on my iOS device. I used to append all data (till end of data) in a memory variable and at the end writing the data using NSStream to a file in my iOS device using method:

stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent

It works fine for small size of data, but the problem is if I am receiving data via web services it could be a big chunk (couple MBs) and I don't want to collect all in memory to write it to the file, to make it efficent I think I have to switch to NSFileHandle to write data in a small chunk size to the same file in several times. Now my question is what is the best approach to do this? I mean how can I do write to the file in BACKGROUND using NSFileHandle? I use code like this:

 - (void) setUpAsynchronousContentSave:(NSData *) data {

       NSString *newFilePath = [NSHomeDirectory()  stringByAppendingPathComponent:@"/Documents/MyFile.xml"];
     if(![[NSFileManager defaultManager] fileExistsAtPath:newFilePath ]) {
         [[NSFileManager defaultManager] createFileAtPath:newFilePath contents:nil attributes:nil];
     }

     if(!fileHandle_writer) {
         fileHandle_writer = [NSFileHandle fileHandleForWritingAtPath:newFilePath];
     }
     [fileHandle_writer seekToEndOfFile];
     [fileHandle_writer writeData:data];

}

but with passing a data size of 1-2 Mb to above method, do I need to make it running in background? FYI I'm writing in main thread.

Answer

vampirewalk picture vampirewalk · Jan 13, 2012

Maybe you can try Grand Central Dispatch.

I spent some time trying it, bellow is my way to do it.

According to Apple's document, if our program need executing only one task at a time, we should create a "Serial Dispatch Queue".So, first declare a queue as iVar.

dispatch_queue_t queue;

create a serial dispatch queue in init or ViewDidLoad using

if(!queue)
{
    queue = dispatch_queue_create("yourOwnQueueName", NULL);
}

When data occurs, call your method.

- (void) setUpAsynchronousContentSave:(NSData *) data { 

    NSString *newFilePath = [NSHomeDirectory()  stringByAppendingPathComponent:@"/Documents/MyFile.xml"];
    NSFileManager *fileManager = [[NSFileManager alloc] init];
    if(![fileManager fileExistsAtPath:newFilePath ]) {
        [fileManager createFileAtPath:newFilePath contents:nil attributes:nil];
    }

    if(!fileHandle_writer) {
        self.fileHandle_writer = [NSFileHandle fileHandleForWritingAtPath:newFilePath];
    }
    dispatch_async( queue ,
                   ^ {
                       // execute asynchronously
                       [fileHandle_writer seekToEndOfFile];
                       [fileHandle_writer writeData:data];
                   }); 
}

At last, we need to release the queue in ViewDidUnload or dealloc

if(queue)
{
    dispatch_release(queue);
}

I combine these code with ASIHttp, and it works. Hope it helps.