I have inherited some code that is in need of a bit of a tidy up. The app has a handful of core sounds and currently, there are lots of AVAudioPlayer instances attached to various ViewController's all playing the same few sounds.
As part of the refactoring exercise, I have decided to implement a singleton class called SoundController. This class will contain one AVAudioPlayer for each sound that needs playing, and instead of each ViewController instantiating their own, they can easily make use of just the one:
[[SoundController controller].majorFunctionSound playAtTime:0];
Another important thing is to ensure that prepareToPlay: has been called on all the sounds prior to them being used to minimise any delays. Since there are only a handful of sounds and they are all likely to be used during any user session, it makes sense to preload all the sounds (on a background thread) when SoundController is first instantiated. In the init method I have something like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, nil, ^(void)
{
NSURL *soundURL = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/MAJOR FUNCTION.m4a", [[NSBundle mainBundle] resourcePath]]];
_majorAudioSound = [[AVAudioPlayer alloc] initWithContentsOfURL:soundURL error:nil];
[_majorAudioSound prepareToPlay];
});
majorAudioSound is (readonly, strong). #synthesize majorAudioSound = _majorAudioSound.
My concerns are around how poorly (or well) this is going to work in terms of concurrency and what I can do to improve the code. Specifically, if I do this:
[[SoundController controller].majorFunctionSound playAtTime:0];
There is clearly a chance that majorFunctionSound wont be initiated properly depending on whether the background initialisation block has completed yet. Is the worse that could happen that the property returns nil and the sound simply doesn't play?
What other issues might there be? Is there a way to always ensure that the AVAudioPlayer has been properly set up?
First of all, I want you to think a second thought on whether your class have to be a singleton just because you intend to only have one instance of it. It is of course one way to do it, but I think that your initialization issues inherits from the fact that you decided to use a singleton class.
Lets say you have a class called SoundManager and you have made it a Singleton class.
When you ask for the instance of the SoundManager anywhere in your app, you will want to assume that the returned instance is ready to be used immediately. If you inside your SoundManager have an init method that is asynchronous, then you do have a design issue, since you should never have to know when you ask for a Singleton if it is the first time or not it has been initialized.
Since the SoundManager requires initialization I would let my app have an instance of the SoundManager in some kind of base class that takes care of the applications flow, instead of making it a Singleton. Either you could just let your AppDelegate instantiate the one and only SoundManager, or you could have a class called ApplicationController or something where you load all the stuff you need for the app when it initializes. Then you can reach the SoundManager instance via this controller class by passing a reference or by letting your ApplicationController be a singleton. Of course this also works if SoundManager is singleton, as long as you make sure you initialize it on startup, but I prefer to have as few singleton classes as possible.
Now to your question about knowing if your sounds are loaded or not.
I would recommend that you load all sounds before you let the user start using the app. In the meanwhile you could show something to the user, like a loading screen, a progressbar, and play sounds/music if you wish. Here is an example of a structure:
Create a class called SoundManager with a property "loaded" that is false from start
Create a class called ApplicationController that instantiate the SoundManager, and other useful classes you might have, like TextureManager or LocationManager etc.
When the app starts, instantiate ApplicationController, which in turn instantiates SoundManager.
Show a loading screen
Let SoundManager load the "loading sound" first and once it is loaded, start playing it
When the loading of sounds is completed, set the "loaded" property to true
When ApplicationController has loaded everything, let the user start using the app by fading out the loading screen.
If you need the user to start using the app even before the sounds are loaded, then you can still use the same approach by having a property called "loaded". Remember to keep the handling of this property synchronized.
Related
I'm not very sure how Document-Based Applications works.
I've created some actions for NSObject in the Mainmenu.xib. One of this is called when the user click on "File>new":
-(IBAction) newDocument:(id)sender{
Document* newDoc =[[Document alloc] init];
[[NSDocumentController sharedDocumentController]addDocument:newDoc];
[newDoc addWindowController: [[NSWindowController alloc] initWithWindowNibName:[newDoc windowNibName] owner:newDoc]];
[newDoc showWindows];
}
I've also this code inside the openDocument:(id) sender action that does the same but of course loading data to define the application workspace.
If I run the application it show a blank document without to call newDocument action. I don't know how to stop default blank document and to set newDocument: to be called.
Then if i do openDocument: too (so I've two documents, one blank and one not) and I do some operation on the second document it also replicate in the first blank one.
I've double check delegates, file owners, and also if the - (void)windowDidBecomeMain:(NSNotification *)notification return different pointers and all seem to be ok.
Probably I've not understood document based application work flow but I've read the Apple guide and other istructions. What do I miss?
An IBAction method is called, when the user did something. So this is not called from the system at app launch.
You can customize the behavior at app launch with -applicationShouldOpenUntitledFile: (NSApplicationDelegate) and – this is probably your next question – -applicationShouldHandleReopen:hasVisibleWindows: (NSApplicationDelegate). Changing the behavior in both cases is not recommended.
Looking to your action method, I see no reason, why you want to customize it.
A instance of your document class is created automatically.
You can create a window controller for it in your document subclass. This is documented.
Just let NSDocumentController do the work for you. What is the problem of the default behavior?
No. I thought to be confused instead the only problem was about releasing observer notification. When you call the close message for a NSDocument notification observers still persist. Working in ARC I miss this point.
So This is the solution at my issue. Thank you anyway.
Lets say I have multiple View controller classes using the same UIAlertView *alertView. *alertView's delegate is set to a centralized delegate.
I do this because would like to use the .tag to do different things based on it.
The question is every time I invoke an alert view or dismiss it, what do i have to do to prevent a memory leak?
Should I not release every time? Or is this a very bad idea?
Thanks.
A UIAlertView may be "shown" from anywhere in your app. I have an app that the main UIViewController has a timer that every so often brings up a UIAlertView. When that timer goes off, even if my main view being shown is from a completely different UIViewController (and thus view) the Alert will come to front.
If you really want to "actively" bring up the UIAlertView from any of your UIViewControllers (lets say based upon a user action), then I would do one of two things.
1) setup my Application Delegate Object with the UIAlertView implemented there, with accessor methods for invoking (showing) the Alert view, and thus freeing it from there also, or
2) Generate a singleton like object with the AlertView implemented there!!!
In either case then you can simply dealloc your UIAlertView once within the dealloc routine you write for either of those placements, and alloc it only once when the object is initialized.
Just treat it like you would any other object. If you want to keep it around, assign it to a retained property like: self.myAlert. You still need to release it like you normally would when creating it. The retained property will take care of keeping it around for you.
Always keep your retains(alloc's, copy's, etc...) and releases balanced.
I currently have a UIViewController and a NSObject class.
What i want to do is to tell the NSObject class to perform an action and then tell the UIViewController when it has finished it's action.
I'm calling the object to perform it's action like so:
[fooObject performActionWithDelegate:self];
The performActionWithDelegate function basically only takes the UIViewcontroller's delegate to perform a callback.
-(void)performActionWithDelegate:(id)d{
// bar is declared in the fooObject header file
// id bar;
[bar setDelegate:d];
[bar performCallback];
}
Where performCallback is a simple NSLog()-statement in the UIViewController:
-(void)performCallback{
NSLog(#"Successfully performed a callback");
{
Now, i'd like this to work. My first guess is that this is not the best approach to this problem.
The full scope of the problem is that the fooObject is supposed to perform a httppost to a webservice to update one of it's properties and then inform the uiviewcontroller if the operation was successful or not.
How do i achieve this?
Any tips and/or pointers will be highly appreciated!
Thanks in advance.
Edit:
The actual problem is that the fooObject is not performing the callback.
Its not clear exactly what you are trying to accomplish. What you are doing seems related to two different design patterns:
Delegate
Asynchronous callback
Delegate: You would use a delegate if there is some reason to separate out some of the functionality of your UIViewController into another object. Instead of the UIViewController doing something it asks another object to do it. This is commonly used for code reuse so you can have the same UIViewController serve in different cases and just change the delegate to change some of its behavior.
Asynchronous callback: This allows an operation to occur in the background while you are doing other things and then be notified by calling a method of your object when the operation completes. You can do this without involving other objects.
In your case, why do you want to perform an HTTP post to a web service outside of our UIViewController? Do you just want to separate the network code from UI code? In this case, you don't really need a delegate, just call the method on the other object from your UIViewController and when it returns, its done. It can pass back any result you need in other parameters. Returning values by setting properties on the calling object is not generally a very good design. Even if you do this the UIViewController isn't really a "delegate".
On the other hand if you are concerned about blocking the main thread while the HTTP post is in process then you will want to use something like asynchronous callback. The easiest way to do this is to use Grand Central Dispatch. Conceptually you could do something like this:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self doLongHTTPPost];
dispatch_async(dispatch_get_main_queue(), ^{ [self longHTTPPostDone]; });
});
This will call doLongHTTPPost in the background and then at some later time after it is complete it will call longHTTPPostDone on the main thread where it is safe to take UI actions.
I have a MainViewController in my Cocoa Touch app which shows a status view containing a UIProgressBar view.
From the MainViewController, FlickrImage model objects are created and iterated over. the FlickrImage objects themselves interact with the Flickr API, which takes time which is why I want the user see the progress bar. The challenge (at least to me it is ;) now is to interact with the UIProgressBar (send progress messages) from the FlickrImage objects. Do I do this using a NSNotificationCenter or can I just declare the MainViewController as a forward class in FlickrImage and interact with the UIProgressBar directly?
The best way is to not have the model call anything at all, but use KVO to observe the model, and then react to the message you get when the model updates. This is because the point of having a separate model layer is that the model shouldn't have to know anything about how data is presented to the user.
So create a class that keeps track of the loading of all the images (most likely you already have such a class) and do something like:
[imageManager addObserver:self forKeyPath:#"progress" options:nil context:nil];
Make sure that the data manager class has a declared property called progress (ideally with values ranging from 0.0 to 1.0) and when you update the value in the manager, you must use the dot notation: self.progress = newVal;
This is much cleaner than sending notifications from the model class. An alternative would be to register the view as a delegate on the manager. There is no clear-cut rule of thumb for when you should use delegates and when KVO is better, although there might be slightly less overhead in using a delegate protocol. Apple uses both approaches but there is a tendency to rely more on KVO, which in my opinion is a good thing.
I prefer NSNotificationCenter, MainViewController register as observe and update the UIProgressBar.
keeping the object MainViewController in FlickrImage and updating UIProgressBar from FlickrImage which make handling UI from model(you are violating MVC)
I'm working on a simple proof-of-concept for an iPhone app (and important bit of info, I'm pretty new to Mac OSX development all around). I created a view based app with a timer. I declared my NSTimer in the interface of my app's controller, used #property and #synthesize, and I initialize it in the controller's viewDidLoad method with scheduledTimerWithTimeInterval method. My selector is a method with the signature -(void)someMethod:(NSTimer *)timer which is declared in the interface and defined in the implementation file of the controller as well. I can step past the line where I assign the timer and see that it points to a valid object, but my program goes no further than the end of the viewDidLoad method and never reaches the breakpoint at the first line of my method that is called by the timer. Also, I see GDB: Program received bad signal: "EXC_BAD_ACCESS" in the status bar of xcode at this point (viewDidLoad end is reached). I didn't do anything in IB but add a view and a picker just so I'd see if the UI actually loads...it never does.
So, am I doing something wrong with the NSTimer, or are my troubles elsewhere? How can I use the debugging tools in xcode to get more information?
EXC_BAD_ACCESS usually indicates a memory management error, without seeing the code probably from somewhere else in your app. It's a very common error for beginners, but an important subject to fully understand, so I'd suggest looking through some of the questions on memory management here and find a few guides or tutorials to look through. It's actually pretty easy to learn.
Also, it shouldn't hurt but unless you need to access the timer in between fire events, you don't actually need to store it as an instance variable. Once you create and start a timer it's added to and retained by the application's run loop.
Have you got NSZombieEnabled?
Might be useful if this is failing on an over released object.