ZipArchive memory problems on iPhone for large archive - iphone

I am trying to compress multiple files into a single zip archive and I am running into low memory warning. Since the complete zip file is loaded into the memory I guess that's the problem. Is there a way by which I can manage the compression/decompression better using ZipArchive so that not all the data is in the memory at once?
Thanks!

After doing some investigation on alternatives to ZipArchive I found another project called Objective-zip that seems to be a little better than ZipArchive. Here is the link:
http://code.google.com/p/objective-zip/
The API is quite simple. One thing I ran into was that in the begging I was reading data and never releasing it so if you are adding a bunch of large files to the zip file remember to release the data. Here is a little code I used:
ZipFile *zipFile = [[ZipFile alloc] initWithFileName:archivePath mode:ZipFileModeCreate];
for(NSString *path in subpaths){
NSData *data= [[NSData alloc] initWithContentsOfFile:longPath];
ZipWriteStream *stream = [zipFile writeFileInZipWithName:path compressionLevel:ZipCompressionLevelNone];
[stream writeData:data];
[stream finishedWriting];
[data release];
}
[zipFile close];
[zipFile release];
I hope this is helpful for anyone who runs into the same issue.

An easier way to deal with this is to simply change ZipArchive's method of reading the file into the NSData. Just change the following code
data = [ NSData dataWithContentsOfFile:file ];
to
data = [ NSData dataWithContentsOfMappedFile:file ];
That will cause the OS to read the file in a memory mapped way. Basically it just uses way less memory as it reads from the file as it needs to rather than loading it all into memory at once.

Related

Write to file without overwriting ObjC

I would like to write text to a file, but when searching for solution, I find "read-append-write" everywhere, but the file is too large for the memory of an iOS device, and it freezes, and resprings.
Is there any other solution to do it?
You can use the NSFileHandle class in order not to have to read the whole file into memory (which is, by the way, bad practice for any file!):
NSFileHandle *fh = [NSFileHandle fileHandleForWritingAtPath:#"/path/to/file.ext"];
[fh seekToEndOfFile];
NSData *data = // obtain an NSData somehow
[fh writeData:data];
[fh closeFile];

Basics Introduction To Using CHCSVParser

I'm implementing CHCSVParser into my iPhone app (thanks Dave!) however I'm really confused on how to use it. I've read the read-me and searched some questions on SO but still not 100% sure what to do.
I have a .CSV file with maybe 5000 rows of data and 3-4 columns.
I want this data to in return, load my UITableView along with its corresponding detailViewController.
So I'm assuming I need to somehow implement the API's array method but can anyone help get me started?
I'm glad you like it :)
Basically, CHCSVParser only parses CSV files. You give it a path to a CSV file, and it'll give you back a whole bunch of NSStrings. What you do beyond that point is entirely up to you.
So let's say you've included a CSV file in your iOS app called "Data.csv". Here's how you'd use CHCSVParser to parse it:
NSString *path = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"csv"];
NSError *error = nil;
NSArray *rows = [NSArray arrayWithContentsOfCSVFile:path encoding:NSUTF8StringEncoding error:&error];
if (rows == nil) {
//something went wrong; log the error and exit
NSLog(#"error parsing file: %#", error);
return;
}
At this point, rows is an array. Each element in rows is itself an array representing a single row in the CSV file. And each element of that array is an NSString.
So let's say your CSV file looks like this:
Barringer,Arizona,United States,Earth
"Chicxulub, Extinction Event Crater",,Mexico,Earth
Tycho,,,Moon
Lonar,Maharashtra,India,Earth
If you run it through the parser, you'll get back the equivalent of this:
[NSArray arrayWithObjects:
[NSArray arrayWithObjects:#"Barringer",#"Arizona",#"United States",#"Earth",nil],
[NSArray arrayWithObjects:#"Chicxulub, Extinction Event Crater",#"",#"Mexico",#"Earth",nil],
[NSArray arrayWithObjects:#"Tycho",#"",#"",#"Moon",nil],
[NSArray arrayWithObjects:#"Lonar",#"Maharashtra",#"India",#"Earth",nil],
nil];
What you do with it then is your business. The CSV parser doesn't know anything about UITableView, so you get to take this data and re-structure it in a way that you're comfortable dealing with and that fits in to your data model.
Also, remember that by using CHCSVParser, you agree to abide the terms under which it is made available. :)

Play a wav file retrieved from a database on the iPhone?

I have alot of wav files stored in sqlite3, but when I retrieve one of them, I can't play it. The retrieve code is
NSData *soundData = (NSDATA *)sqlite3_column_blob(statement, 0);
mPlayer = [[AVAudioPlayer alloc] initWithData:soundData error:&error];
The data is stored as binary and it's there when I search for it using sqlite3.
Sorry. Never mind. I just compressed the data more and it works fine now. Seems the number of files is not as important as their size afterall.

Simplest way on iPhone to unzip downloaded file?

Goal: download a zipped file, unzip it, and save it in the iPhone app's Documents directory.
The following code makes use of the initWithGzippedData method that was added to NSData in the Molecule app found here:
http://www.sunsetlakesoftware.com/molecules
As adapted to my app:
NSString *sFolder = [NSHomeDirectory() stringByAppendingPathComponent:#"Documents"];
NSString *sFileName = [sFolder stringByAppendingPathComponent:#"MyFile.db"];
NSURL *oURL = [NSURL URLWithString: #"http://www.isystant.com/Files/MyFile.zip"];
NSData *oZipData = [NSData dataWithContentsOfURL: oURL];
NSData *oData = [[NSData alloc] initWithGzippedData:oZipData];
[oZipData release];
b = [oData writeToFile:sFileName atomically:NO];
NSLog(#"Unzip %i", b);
Result: A zip file is successfully downloaded. From it a new, supposedly unzipped file is created in the Documents directory with the desired name (MyFile.db) but it has zero bytes.
Anybody see the problem? Or else is there a simpler way to unzip a downloaded file than the one used in the Molecules app?
I think that your problem may be that you are attempting to gzip-deflate a Zip file. Those are two different compression algorithms.
I based the gzip-deflating code in Molecules on this NSData category (the code of which I've copied into this answer) provided by the contributors to the CocoaDev wiki. What you'll want to do is use their -zlibDeflate implementation, which should properly unzip a Zip file.
Unrelated to your problem, instead of using NSHomeDirectory() and appending a path component, the recommended approach for finding the documents directory is the following:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
You should make sure your file is never too big, as you are loading it fully into memory before the unzip starts.

Loading data files in iPhone project

How does one read a data file in an iPhone project? For example, lets say I have a static file called "level.dat" that is structured as follows:
obstacles: 10
time: 100
obstacle1: 10,20
...
I would like to read the contents of the file into a NSString then do the parsing. How do I read the contents of a file into a string? Also, where in the project should the "level.dat" file reside? Should it be under "Resources" or just in the main directory?
Thanks in advance!
See this answer: How to fopen() on the iPhone? which shows how to get access to resources in your bundle. Once you have the path, just use [NSString stringWithContentsOfFile:encoding:error:].
NSString *path = [[NSBundle mainBundle] pathForResource: #"level" ofType: #"dat"]
NSError *error = nil;
NSString *data = [NSString stringWithContentsOfFile: path
encoding: NSUTF8StringEncoding
error: &error];
While this isn't what you asked for, consider turning your files into plists. You will have to reformat them into XML, but then you can load them straight into a NSDictionary with:
dict = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"levels" ofType:#"plist"]];
Have you considered putting the data in an SQLite database instead of a flat file? I find that the API is very easy to use on the iPhone.
It is how I do all of my data storage on the phone now.
If you need help parsing the data string, there's a helpful article on Cocoa For Scientist