TAGS :Viewed: 17 - Published at: a few seconds ago

[ iPhone: Do several allocs create new objects if assigned to the same property? ]

As I am relatively new to the world of OOP, I was wondering what mayhem I might potentially be causing by constantly alloc'ing something to a property.

I have a property:

@property (nonatomic, retain) AVAudioPlayer *audioPlayer;

This is then synthesized in my viewcontroller.m file. Now, I have a method to initialize the audioPlayer by setting a URL for it and such. The thing that's worrying me is that I call this initialize method every time a new sound is selected (to allow the user to play a sound from a picker):

self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];

The code above shows the line that is called every time the picker value is changed.

Before I blabber on too much, does the above line a) create a NEW instance of the AVAudioPlayer object every time I call it, or does it b) merely "overwrite" the already existing audioPlayer instance? If a), I suspect I could kill the memory rather quickly; what would I do to make it efficient? And if b), I guess that's fine then? ...and if secret answer c) (you are totally misunderstanding how objects are created), then please shine a light on my ignorance.


Answer 1

self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];

should either be:

self.audioPlayer = [[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil] autorelease];


AVAudioPlayer *aPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
self.audioPlayer = aPlayer;
[aPlayer release];

Some details added for clarity.

I think the confusing part is the self.audioPlayer right? It might help to think of it as a method, which is what a setter actually is.

These lines are equivalent:

self.audioPlayer = aPlayer;
[self setAudioPlayer:aPlayer];

And since the property is set to "retain", the method setAudioPlayer has a [aPlayer retain] line built into it. So if you don't release the local copy of aPlayer it is being retained twice.

Hope that helps.

Answer 2

Each time you call alloc it creates a new instance of the object. However, your property with the "retain" attribute will take the previously stored value and call release on it.

So for your reference counting, alloc/init = +1 and release = -1. What you need to do is worry about decrementing the last copy that is created when you are done with it.

Answer 3

You should avoid using properties when assigning newly allocated objects. For example, if your audioPlayer property is synthesised to read and write to a member variable called mAudioPlayer, then you should do this:

mAudioPlayer = [[AVAudioPlayer] alloc] initWithContentsOfURL:fileURL error:nil]

If you have to use the property (because of side effects) then try this:

AVAudioPlayer* audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
self.audioPlayer = audioPlayer;
[audioPlayer release];

This is safe because the property automatically retains the newly created object so you can release it locally afterwards. So if you do the way you mentioned there will be a leak. You'd have a reference of 2 (1 from the alloc, and 1 from the property retaining it). If you set self.audioPlayer to 0, or another instance, you'd only release it once.

Hope that helps.

Answer 4

It's c)

Here is the problem, everytime an alloc is called, the retain count of the object created is incremented to 1. Now, when you assign it using a self. and considering you have declared the property as

@property (nonatomic, retain) AVAudioPlayer *audioPlayer;

the retain count is increased to 2.

When you assign another object to the variable audioplayer, the older instance's retain count is decremented by 1. Thus the older object will merely leak.

What you would need is

self.audioPlayer = [[[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil] autorelease];

provided you have added a release call to audioplayer in your dealloc method.