Can I create a single object that's accessible by different methods?

69 Views Asked by At

This might be a basic question, but I'm new to Cocoa and Objective-C and OOP in general, and I haven't been able to find an answer to this anywhere.

In an app I'm writing, I want to play a sound file whenever the user presses a particular button. I'm using NSSound for that and have no problem implementing it. The problem is, I only know how to do it by creating a new NSSound object each time the button is pressed:

- (IBAction)playSound:(id)sender {
    NSSound *sound = [[NSSound alloc] initWithContentsOfFile:kSoundFilePath byReference:YES];
    [sound play];
}

This is an issue for me because if the user clicks the button repeatedly before the file finishes playing, it will create new instances of NSSound and they will all play on top of each other, which I don't want. Is there a way that I can create the NSSound object outside of that method, and have the IBAction method for the button check to see if the NSSound is playing before telling it to play again?

2

There are 2 best solutions below

14
SevenBits On BEST ANSWER

Yessir. This can be done several ways. One easy way is to use a private property:

/* these are in the SAME FILE */
@interface MyClass ()

    @property (nonatomic, retain) NSSound *sound;

@end

@implementation MyClass

    - (IBAction)playSound:(id)sender {
        self.sound = [[NSSound alloc] initWithContentsOfFile:kSoundFilePath byReference:YES];
        [self.sound play];
    }

@end

You can also do it this way. EDIT: As Avt stated in the comments, there are some issues when using a global variable in his fashion. If you ever will be creating multiple instances of this class, you might be better off using a singleton design pattern. To explain, here's an article by the venerable Mattt Thompson.

@implementation MyClass

    NSSound *sound;

    ...

    - (IBAction)playSound:(id)sender {
        sound = [[NSSound alloc] initWithContentsOfFile:kSoundFilePath byReference:YES];
        [sound play];
    }

@end

I personally use the first way because it makes it more clear from a programming standpoint that your class owns the created object. Though the second way is legal, it is less clear where the object belongs... it could be a local variable in the method's scope, or something else. I would highly recommend the first way, but in the interest of education you should know about all possible ways.

0
Linuxios On

Up at the declaration of your class, which should look something like this:

@implementation MyViewController

Add this:

@implementation MyViewController {
  NSSound* sound;
}

Then in viewDidLoad:

sound = [[NSSound alloc] initWithContentsOfFile:kSoundFilePath byReference:YES];

And finally:

- (IBAction)playSound:(id)sender {
    [sound play];
}