I need a hint on how to write multiple NSData chunks to single file. Downloading a file using NSURLConnection in chunks. Each chunk is downloaded in a separate NSOperation thread. As the chunks finish downloading they need to be written to a file so combined result is the file downloaded.
What would be the best way to manage the NSData that is returned and writing it to a single file?
No need to eat up memory by creating a large NSMutableData object (not possible with a large download anyway as you would run out of memory), nor do you need to waste time creating tons of small files and concating them (could take a very long time with a large file, iDevice disk IO is not very fast).
Just create an NSFileHandle and use it to write each NSData object to the end of the file as they come in.
NSFileHandle *handle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
[handle seekToEndOfFile];
[handle writeData:dataPiece];
[handle closeFile];
You'll need to first create the file though so that you can open it with the NSFileHandle. To do that you can just write the first NSData piece using the following, then write the rest using the file handle.
[dataPiece writeToFile:filePath atomically:YES];
edit: Actually I just re-read the question and realized you're using separate threads to download the chunks so they may not finish in order, so my solution won't work. I must have been tired the day I answered and skipped right over it. But, hopefully my answer can at least help others that are downloading chunks in order or single threaded.
Write each chunk to a separate file. Then when the last chunk is downloaded, concatenate each file in the correct order into a single large file.
Assuming that you know what the final data size will be, and you want to stick to Foundation classes/Objective-C, you could create an instance of NSMutableData that can be shared across these operations. When an operation completes its chunk, it should lock some shared mutex, write its completed download to the appropriate place in the NSMutableData object and then unlock the shared mutex.
Once all the operations are joined, you can simply write the mutable data to a file using the writeToFile: convenience methods on the NSData class. If you're more proficient in C/BSD, you could also create the file as an mmap and simply write to it. Since all the data are going to discrete seconds in the mapping, you can write without locking a mutex. Once all the operations are joined, you can remove the mmap and close the file.
Related
I'm setting up NSFileCoordinator and NSFilePresenter in my app so I can do file IO from my AppleWatch app safely. There are some places in my code where I write to a file a couple of times in quick succession. This is a problem in and of itself and I'm working to correct it, but I'm noticing some weird behavior in the process.
I wrap my writes like this:
//In a class that implements NSFilePresenter:
NSFileCoordinator *coord = [[NSFileCoordinator alloc]initWithFilePresenter:self];
[coord coordinateWritingItemAtURL:self.presentedItemUrl options:0 error:nil byAccessor:^(NSURL *url)
{
//do my writing here using CFWriteStreamRef or NSOutputStream
}];
On the first write, the write block happens within 1 ms. But after that, there's about a 0.5 second delay between calling coordinateWritingItemAtURL and the write block being executed.
Is this expected behavior?
Some of the documentation for NSFileCoordinator and NSFilePresenter says to use prepareForReadingItemsAtURLs:writingItemsAtURLs:options:error:byAccessor: for batch operations, but it seems weird to get such a long delay when I don't batch.
Update: This happens with reading too.
Update 2: Here is an example project reproducing the problem.
Update 3: Using this API for coordination between an app and its extension is apparently a bad idea. But the question still stands.
Referring to File System Programming Guide , you can read following:
you might want to avoid incorporating changes directly from your file
presenter method. Instead, dispatch a block asynchronously to a
dispatch queue and process the changes at a later time. This lets you
process the changes at your app’s convenience without causing
unnecessary delays to the file coordinator that initiated the change.
Of course, when saving or relinquishing control of a file (such as in
the relinquishPresentedItemToReader:,
relinquishPresentedItemToWriter:, or
savePresentedItemChangesWithCompletionHandler: methods) you should
perform all necessary actions immediately and not defer them.
I think this is your case where you are defering actions.
Possible Solution:
Please read this well , to properly handle multiple successive writing operations , the relinquishPresentedItemToWriter , can do the job , same will work with reading file , relinquishPresentedItemToReader , supposing that multiple different objects are trying to read and write the same file.
P.S :
I dont know what your app does exactly , but i hope you have read this :
If you are implementing a document-based app, you do not need to
incorporate file presenter semantics into your NSDocument subclasses.
The NSDocument class already conforms to the NSFilePresenter protocol
and implements the appropriate methods. Thus, all of your documents
automatically register themselves as presenters of their corresponding
file and do things like save changes and track changes to the
document.
Is it possible to use options NSFileCoordinatorReadingImmediatelyAvailableMetadataOnly for reading and NSFileCoordinatorWritingContentIndependentMetadataOnly for writing in some cases? Looks like this iOS8 options can help you.
I have a few game sounds, such as four different beep tones, and each one is played depending on the action the user performs. Do I need to instantiate four different AVAudioPlayer objects?
Or do I just need one main player? If I just need one, then how do I switch the file URL?
This is what I am doing now:
NSError *error;
soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:markedSound error:&error];
but it seems to take a few seconds to initialize. Is there a way to preload the files and switch between them quickly?
Continue creating as many instance as you have sounds.
After initialization, use the method prepareToPlay:
Prepares the audio player for playback by preloading its buffers.
Discussion Calling this method preloads buffers and acquires the audio
hardware needed for playback, which minimizes the lag between calling
the play method and the start of sound output.
Calling the stop method, or allowing a sound to finish playing, undoes
this setup.
For example:
NSError *error;
soundPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:markedSound error:&error];
[soundPlayer prepareToPlay];
For the moments, you only need 4 songs, so this would be premature optimization. But if later, the number of sounds you need increase a lot, you can wrap all this in a sub-class and do lazy loading. As the documentation states, using the stop method is the equivalent of unloading your file.
You don't necessarily need to instantiate four different AVAudioPlayer objects, although this is one option (that may be simplest if you only have four sounds).
One you could make would be to load the sounds into memory in advance, which you can do by using the initWithData method instead of the initWithContentsOfURL (having loaded all your sound files in advance to NSData objects). This may speed things up slightly, although it also depends on your sound format. Uncompressed files will obviously be loaded much quicker than an MP3, for example.
If this still doesn't work out for you, your best bet would be to look at some of the lower level audio technologies provided, which are more suited for games work (OpenAL, Audio Toolbox, etc), but these are a fair bit more complex than a simple AVVAudioPlayer.
I can go either way on this for this project, but I'm curious if using a plist to store some data is going to be more or less efficient than just keeping a plist in the documents folder. The data is about 50 strings/dictionaries.
In both cases the data gets persisted using some file IO so disk access should be similar.
However, the plist seems like a little more work.
NSUserDefaults is a plist (that is why only plist types can be stored in it). So ultimately there isn't going to be much difference in efficiency (whatever you mean by that). Your consideration should rather be where it is appropriate to keep this data. Don't keep it in the Document folder unless it is appropriate for storage in iCloud, says Apple; it will be backed up when the user backs up the device, and will subtract from the user's quota, so you need to be sparing of what you keep there.
In one of my own apps, where I download a bunch of data from an RSS feed and present it to the user, I store the data in the user defaults, because it is part of the app's persistent state the next time it appears. My data isn't a document; it's the app's state. That's my reasoning, and I'd suggest you might reason along similar lines...
In my opinion, plist are much simpler to use than NSuserDefaults. Afterall, a dictionary can save itself as a plist. As for efficiency, they sould be the same as NSUserDefaults stores everything as a plist but provides more services such as comparing which key/values pair have changed compared to a provided set of key/values default pairs.
You may want to consider JSON using JSONKit. Some tests show it's faster than a binary plist, if speed is your primary concern. The API is dead simple because it creates a category on NSDictionary and NSArray. Calling -(NSData *)JSONData on either of those objects returns an NSData object ready to save.
My game comes with lots of .plist files containing many dictionaries, arrays, etc. They are about each party member's stats, info, etc.
A singleton is in charge of reading all those files and provide the necessary data for my game to run.
The game, at many points, will edit such values from the singleton, like a hero's level, stats, etc.
Eventually, the player will be given the option to "save" the game. My original idea was to basically tell the singleton to "overwrite" the .plist files within the project with the new edited data (the new data might have changes in stat entries etc, or even brand-new dictionaries representing new party members etc) and done.
But, I don't know how to do that, or even if it is possible.
I heard about NSUserDefaults, but I don't know if it suitable for what I am aiming, considering that the amount of dictionaries and arrays will be quite big, and that it is possible to keep adding up more dictionaries and arrays in the future. Imagine that the party can have as many members as you wish, for instance.
*Note: there are no multiple save files/profiles. When you press save, it saves, without asking anything else.
NSUserDefaults is not suitable for any substantial amount of data. Just write the plist to the documents directory, specify atomic:YES to insure the entire file is written.
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
If YES, the array (or dictionary) is written to an auxiliary file, and
then the auxiliary file is renamed to path. If NO, the array is
written directly to path. The YES option guarantees that path, if it
exists at all, won’t be corrupted even if the system should crash
during writing.
This method exists for both NSArray and NSDictionary,
Obtain the document directory:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
BOOL status = [NSDictionary writeToFile:filePath atomically:YES];
NSDictionary could have ten NSArray depending on the top element of the plist.
Consider using CoreData.
Do I have this right ...
To manipulate files on disk (create, copy, rename etc.) you use NSFileManager
To manipulate file contents (open, read, close etc.) you use NSFileHandle
I just want to make sure I am understanding this right.
EDIT_001
Thanks, thats what I figured Joshua, so I am assuming that by using the example below, open and close are both handled automatically by the implementation.
fileContents = [NSString stringWithContentsOfFile:fileOnDisk
encoding:NSMacOSRomanStringEncoding
error:&fileError];
gary
More or less, yes. From the docs:
NSFileHandle objects provide an object-oriented wrapper for accessing open files or communications channels.
... though NSFileHandle isn't necessary to read/write files. You can write an NSString to / read from a file with one line of code and no handle. Depends on what you want to do.
I believe, for the most part, that NSFileHandle is a wrapper around file descriptors -- good for 'relatively' low level reading or writing.
If you are just pushing the contents of a string or NSData to or from a file the associated methods on those classes are hard to beat.