3

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.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Cam
  • 275
  • 6
  • 24

1 Answers1

8

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.

vampirewalk
  • 819
  • 8
  • 19
  • it does't work! In case I call "setUpAsynchronousContentSave" for second time it would not be right way to use the same NSFileHandle to append data to the same file, the first time could be still on the process. – Cam Jan 16 '12 at 22:16
  • I'll check it tomorrow again. Thank you – Cam Jan 17 '12 at 23:10
  • If you use DefaultFileManager it runs on the mainThread, if you alloc/init your own fileManager than it is bound to the thread on which it is created on. The threading rules are the same as they are for NSManagedObjectContexts. – TheM00s3 Apr 13 '15 at 21:39
  • NSFileHandle is not thread safe. The file descriptor (int I think) can be destroyed in your example. – Ricardo Anjos Apr 19 '17 at 12:55