Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Objective-C Category and new iVar

I try to extend the functionality of SimpleAudioEngine of cocos2d with the ability to play several sound effect one after another as some kind of chain. I tried to do this with an extension. However I now realized that I probably also need an iVar to remember the names of all sound files and one to remember which sound is currently playing.

However it seems that I cannot add iVars in a category. Instead I tried to use an extension, but it seems that they need to be in the original .m file of the class so that also would not work. Is there yet another way, that allows me to do this?

The header with the category

#import <Foundation/Foundation.h>
@interface SimpleAudioEngine(SoundChainHelper)<CDLongAudioSourceDelegate>
-(void)playSoundChainWithFileNames:(NSString*) filename, ...;
@end

And the .m-file with the extension:

#import "SoundChainHelper.h"

@interface SimpleAudioEngine() {
    NSMutableArray* soundsInChain;
    int currentSound;
}
@end

@implementation SimpleAudioEngine(SoundChainHelper)

// read in all filenames and start off playing process
-(void)playSoundChainWithFileNames:(NSString*) filename, ... {
    soundsInChain = [[NSMutableArray alloc] initWithCapacity:5];

    va_list params;
    va_start(params,filename);

    while (filename) {
        [soundsInChain addObject:filename];
        filename = va_arg(params, NSString*);
    }
    va_end(params);
    currentSound = 0;
    [self cdAudioSourceDidFinishPlaying:nil];
}

// play first file, this will also always automatically be called as soon as the previous sound has finished playing
-(void)cdAudioSourceDidFinishPlaying:(CDLongAudioSource *)audioSource {
    if ([soundsInChain count] > currentSound) {
        CDLongAudioSource* mySound = [[CDAudioManager sharedManager] audioSourceForChannel:kASC_Right];
        [mySound load:[soundsInChain objectAtIndex:0]];
        mySound.delegate = self;
        [mySound play];
        currentSound++;
    }
}

@end

Alternatively I tried to define the iVars as properties, which will compile. However I can neither synthesize them nor do I have any other possibility to bind them to any method.

I try to implement the functionality as a category of SimpleAudioEngine so that I only need to remember one class that deals with all my sound issues. and so that I can create a chain as simple as this:

[[SimpleAudioEngine sharedEngine] playSoundChainWithFileNames:@"6a_loose1D.mp3", @"6a_loose2D.mp3", @"6a_loose3D.mp3", @"6a_loose4D.mp3", @"6b_won1D.mp3", nil];

If there is another way that yields the same/ a similar result I would also be very thankful.

like image 299
gebirgsbärbel Avatar asked May 08 '12 16:05

gebirgsbärbel


People also ask

What is iVar Objective-C?

For a private/protected variable, use iVar; for a public variable, use property. If you want to use the benifit of property attributes for a private variable, like retain, nonatomic etc., declare the property in the implementation file as a private property.

What is a category Objective-C?

A category allows you to add methods to an existing class—even to one for which you do not have the source. Categories are a powerful feature that allows you to extend the functionality of existing classes without subclassing. Check the apple doc for the Category in Objective-C.

What is a category in Objective-C and when is it used?

Categories provide the ability to add functionality to an object without subclassing or changing the actual object. A handy tool, they are often used to add methods to existing classes, such as NSString or your own custom objects.

What is the difference between category and extension in Objective-C?

Category and extension both are basically made to handle large code base, but category is a way to extend class API in multiple source files while extension is a way to add required methods outside the main interface file.


1 Answers

You are correct that you can't add instance variables (or synthesized @properties) to a category. You can workaround this limitation using the Objective-C runtime's support for Associative References

Something like this:

In your .h:

@interface SimpleAudioEngine (SoundChainHelper)
    @property (nonatomic, retain) NSMutableArray *soundsInChain;
@end

In your .m:

#import <objc/runtime.h>
static char soundsInChainKey;

@implementation SimpleAudioEngine (SoundChainHelper)

- (NSMutableArray *)soundsInChain
{
   return objc_getAssociatedObject(self, &soundsInChainKey);
}

- (void)setSoundsInChain:(NSMutableArray *)array
{
    objc_setAssociatedObject(self, &soundsInChainKey, array, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end

(The standard disclaimer applies. I typed this in the browser, and didn't test it, but I have used this technique before.)

The documentation I linked to has a lot more information about how associative references work.

like image 80
Andrew Madsen Avatar answered Sep 22 '22 04:09

Andrew Madsen