I have an instance of AVAudioPlayer that will play several sounds. Is there a way to provide an AVAudioPlayer instance with a new sound, or do I have to create a new instance with initWithData:?
There's no API to pass a new file to an existing AVAudioPlayer instance. However, AVAudioPlayer creation isn't expensive, so you shouldn't be concerned about performance, as long as you release instances once you're done with them.
If you have a fixed set of sounds that play over each other, or are likely to play repeatedly, you can create one AVAudioPlayer instance for each sound and re-use those instances by calling play on the appropriate instance.
If only one sound is playing at a time, and you're generating new sounds (for instance with text-to-speech based on user input), I generally have a single player as a property. When a new sound plays, I call [player stop], release the old player, and instantiate a new one with the new sound file.
Reusing objects is usually a bad idea, generally try to have your objects as short-lived as possible. This reduces the number of possible states of your code and makes reasoning about it easier. (“Where did this value come from? Maybe it’s a leftover from the previous instance usage?” Yuck.) Only break this rule once you run into measurable problems.
For further reading along this line of thought you can browse questions tagged “immutable”.
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 Game class that has a -(void) play method which will be executed when the user clicks on the Play button on the device.
Inside the -(void) play method I have a while loop that will be executed repeatedly until the user clicks on the Quit button. This while loop is basically the core of my code, where all necessary methods are being called, things happen, objects interact etc.
I also have a User class (amongst other classes..) and I create a User* player instance in the -(void) play method of my Game class to store some values and have those interact with other things along the duration of the game..
Now I need to know (at any moment during the game..) the device's deviation from the magnetic North & the acceleration the user is exercising on the device
I've written the code and everything is working fine. However, being new to programming I have a few questions concerning the overall design of my code, which I think is a mess especially when it comes to using the CoreLocation & CoreMotion frameworks..
The -(void) play method of the Game class (which is basically my "main" method) is executed on a separate thread as in [game performSelectorInBackground:#selector(play) withObject:nil]; Is this the right way to do it?
However, I initialise CoreMotion Acceleration updates from inside the -(void) play method as in [motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue]withHandler:^(CMDeviceMotion *motion, NSError *error){...} which means that updates will be stored in the main Queue whereas the method initializing these updates is executed from inside a method(-(void) play) that runs on a separate thread. Does that make sense?
I also initialise CoreLocation updates when I initialize an instance of my Game class. Even more weird?
My point is this. Given that I'll be measuring the acceleration the user is exercising on the device and the orientation he/she is giving to the device (degrees) I want to encapsulate all that in my User class and have methods like [player getMyDegrees]; and [player getMyAcceleration]; Isn't this the correct way design-wise? Where exactly should I initialize those updates? From inside which specific class-method? Should everything be running on the same main thread or the same separate thread or on different separate threads? I'm confused..
Threading is a complex issue, as you've no doubt seen.
The -(void) play method of the Game class (which is basically my
"main" method) is executed on a separate thread as in [game
performSelectorInBackground:#selector(play) withObject:nil]; Is this
the right way to do it?
It depends. Is there any work that you are doing in the -play method that absolutely must be taken off the main thread (i.e. crazy number crunching, server requests, loading textures, etc.) so as to make the UI more responsive? That's really the only reason methods are dispatched to the background on iOS, is to maintain a responsive UI, because UIKit classes are not just thread-unsafe, but they will wholeheartedly dive straight off a cliff and take your app with them if you try to do anything in the background.
However, I initialise CoreMotion Acceleration updates from inside the
-(void) play method ... which means... these updates are executed from inside a method(-(void) play) that runs on a separate thread. Does
that make sense?
Well, again it depends here. The ultimate goal of threading is not to have two or more threads attempt to access or mutate one object held by another thread, which is just the start of a whole mess of problems. The one thing that I have to take issue with is the fact that you are using the main queue despite a very clear and ominous warning in the documentation:
Because the processed events might arrive at a high rate, using the
main operation queue is not recommended.
This is an example of one of those high powered calculations that you should be throwing to a background thread, which is NSOperationQueue's forte.
I want to encapsulate all that in my User class and have methods like
[player getMyDegrees]; and [player getMyAcceleration]; Isn't this the
correct way design-wise? Where exactly should I initialize those
updates? From inside which specific class-method?
It depends on how design-wise you're trying to be, and what you consider good design. Good code is usually code that you could hand off to another programmer and have them pick up where you started off with very little effort. That being said, it sounds like your user class is going to be the controller part of MVC. If your classes are too bulky, cut 'em up. Classes in objective-c are surprisingly light-weight creatures. Even arranging one class in multiple categories that describe the general section's implementation are fine (the header of NSString for example).
Should everything be running on the same main thread or the same
separate thread or on different separate threads? I'm confused..
Everything UI and light should be running in the main thread (or main queue if that's how you prefer to look at it), and everything heavy and thread-blocking should be put in a background thread.
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 am attempting to write an app that reads images from the asset library, modifies the image's GPS data and writes it back to the asset library. I store the assets in a mutableArray via the "enumerating assets" methods. Most of the details on how to do the various steps, I got from searching this forum. Thanks!
I have found that when I write the first "asset" via the "writeimagedatatosavedphotosalbum" method, all the elements of the mutableArray associated with the assets' URL became null. Furthermore, I noticed that writing back an image does not replace the original image, but instead creates a second instance of the image.
Just thought I'd pass these results along, in case others had questions. And, of course, I'd be interested in other's comments, observations, etc.
This forum has provided me with great information. Thanks again.
Your ALAsset object is only as good for the amount of time that your ALAssetsLibrary object is around. You either need to do everything you want in the completion block when you get the ALAsset, or store the ALAssetsLibrary in an instance variable so ARC does not deallocate it.
An ALAsset is essentially a Core Data object who can have properties accessed from multiple threads but a NSManagedObject or a subclass of NSManagedObject does not make sense without a parent NSManagedObjectContext much in the same way an ALAsset doesn't make sense without an ALAssetsLibrary.
It is common practice to store the NSManagedObjectContext on the AppDelegate; and while I abstract that functionality into a wrapper/singleton there is a retained reference to the NSManagedObjectContext throughout the app lifecycle. Apply the same logic to the ALAssetsLibrary and everything will works as expected.
Sorry for long description, however the questions aren't so easy...
My project written without GC. Recently I found a memory leak that I can't find. I did use new Xcode Analyzer without a result. I did read my code line by line and verified all alloc/release/copy/autorelease/mutableCopy/retain and pools... - still nothing.
Preamble: Standard Instruments and Omni Leak Checker don't work for me by some reason (Omin Tool rejects my app, Instruments.app (Leaks) eats too many memory and CPU so I have no chance to use it).
So I wanna write and use my own code to hook & track "all" alloc/allocWithZone:/dealloc messages statistics to write some simple own leaks checking library (the main goal is only to mark objects' class names with possible leaks).
The main hooking technique that I use:
Method originalAllocWithZone = class_getClassMethod([NSObject class],#selector(allocWithZone:));
if (originalAllocWithZone)
{
imp_azo = (t_impAZOriginal)method_getImplementation(originalAllocWithZone);
if (imp_azo)
{
Method hookedAllocWithZone = class_getClassMethod([NSObject class],#selector(hookedAllocWithZone:));
if (hookedAllocWithZone)
{
method_setImplementation(originalAllocWithZone,method_getImplementation(hookedAllocWithZone));
fprintf(stderr,"Leaks Hook: allocWithZone: ; Installed\n");
}
}
}
code like this for hook the alloc method, and dealloc as NSObject category method.
I save IMP for previous methods implementation then register & calculate all alloc/allocWithZone: calls as increment (+1) stat-array NSInteger values, and dealloc calls as decrement (-1).
As end point I call previous implementation and return value.
In concept all works just fine.
If it needs, I can even detect when class are part of class cluster (like NSString, NSPathStore2; NSDate, __NSCFDate)... via some normalize-function (but it doesn't matter for the issues described bellow).
However this technique has some issues:
Not all classes can be caught, for
example, [NSDate date] doesn't catch
in alloc/allocWithZone: at all, however, I can see alloc call in GDB
Since I'm trying to use auto singleton detection technique (based on retainCount readind) to auto exclude some objects from final statistics, NSLocale creation freezes on pre-init stage when starting of full Cocoa application (actually, even simple Objective-C command line utility with the Foundation framework included has some additional initialization before main()) - by GDB there is allocWithZone: calls one after other,....
Full Concept-Project draft sources uploaded here: http://unclemif.com/external/DILeak.zip (3.5 Kb)
Run make from Terminal.app to compile it, run ./concept to show it in action.
The 1st Question: Why I can't catch all object allocations by hooking alloc & allocWithZone: methods?
The 2nd Question: Why hooked allocWithZone: freezes in CFGetRetainCount (or [inst retainCount]) for some classes...
Holy re-inventing the wheel, batman!
You are making this way harder than it needs to be. There is absolutely no need whatsoever to roll your own object tracking tools (though it is an interesting mental exercise).
Because you are using GC, the tools for tracking allocations and identifying leaks are all very mature.
Under GC, a leak will take one of two forms; either there will be a strong reference to the object that should long ago been destroyed or the object has been CFRetain'd without a balancing CFRelease.
The collector is quite adept at figuring out why any given object is remaining beyond its welcome.
Thus, you need to find some set of objects that are sticking around too long. Any object will do. Once you have the address of said object, you can use the Object Graph instrument in Instruments to figure out why it is sticking around; figure out what is still referring to it or where it was retained.
Or, from gdb, use info gc-roots 0xaddr to find all of the various things that are rooting the object. If you turn on malloc history (see the malloc man page), you can get the allocation histories of the objects that are holding the reference.
Oh, without GC, huh...
You are still left with a plethora of tools and no need to re-invent the wheel.
The leaks command line tool will often give you some good clues. Turn on MallocStackLoggingNoCompact to be able to use malloc_history (another command line tool).
Or use the ObjectAlloc instrument.
In any case, you need to identify an object or two that is being leaked. With that, you can figure out what is hanging on to it. In non-GC, that is entirely a case of figuring out why it there is a retain not balanced by a release.
Even without the Leaks instrument, Instruments can still help you.
Start with the Leaks template, then delete the Leaks instrument from it (since you say it uses too much memory). ObjectAlloc alone will tell you all of your objects' allocations and deallocations, and (with an option turned on, which it is by default in the Leaks template) all of their retentions and releases as well.
You can set the ObjectAlloc instrument to only show you objects that still exist; if you bring the application to the point where no objects (or no objects of a certain class) should exist, and such objects do still exist, then you have a leak. You can then drill down to find the cause of the leak.
This video may help.
Start from the Xcode templates. Don't try to roll your own main() routine for a cocoa app until you know what you're doing.