Alexis King
Alexis King

Reputation: 43862

Is there anything like a Java enum in Objective-C?

I have a situation in Objective-C where a Java-style enum would be very helpful. I have a set of layer types, and each layer has its own persistence value associated with it (stored in seconds). I want to keep these layer types in an enum, since they're too similar to make into separate classes, like so:

typedef enum {
    ExplosionLayerType,
    FireworkLayerType,
    FireLayerType,
    FireJetLayerType
} FXLayerType;

In Java, I could easily associate these two values with something like this:

public enum FXLayerType {
    Explosion(3),
    Firework(6),
    Fire(7),
    FireJet(-1);

    private int persistence;

    FXLayerType(int persistence) {
        this.persistence = persistence;
    }
}

Is there a simple way to create a sort of lightweight class like this in Objective-C, or will need to resort to more primitive methods?

EDIT:

Various people have suggested doing something like this:

typedef enum {
    ExplosionLayerType = 3,
    FireworkLayerType = 6
} FXLayerType;

This will not work for me, since I may have something like this (Java-style enum):

Explosion(3),
Firework(6),
Dust(3);

In Java, Dust and Explosion will be treated as unique values, but direct assignment with C enums will treat them as being exactly the same.

Upvotes: 4

Views: 1903

Answers (6)

Robert Schmid
Robert Schmid

Reputation: 454

The struct based answers look good on their face but fail when you try to add Objective-C objects into the struct. Given that limitation, truly emulating the Java-style enum may be more work than it is worth.

Upvotes: 0

Navid Mitchell
Navid Mitchell

Reputation: 1445

I have recently used the j2objc format for enums. It works rather nicely. Additionally you can auto generate your enums if you are trying to map directly from a java object.

https://code.google.com/p/j2objc/wiki/Enums

However, I did remove the j2objc specific classes from my "Enums". I did not want the additional dependencies.

Upvotes: 0

CRD
CRD

Reputation: 53010

To emulate Java enum's we need something which is comparable (can be operands of == etc.), which can have fields, and is lightweight. This suggests structs, and optionally pointers to structs. Here is an example of the latter:

FXLayerType.h:

typedef const struct { int persistence; } FXLayerType;

extern FXLayerType * const LayerTypeExplosion;
extern FXLayerType * const LayerTypeFirework;
extern FXLayerType * const LayerTypeDust;

FXLayerType.m:

#import "FXLayerType.h"

const FXLayerType _LayerTypeExplosion = { 3 };
const FXLayerType _LayerTypeFirework = { 6 };
const FXLayerType _LayerTypeDust = { 3 };

FXLayerType * const LayerTypeExplosion = &_LayerTypeExplosion;
FXLayerType * const LayerTypeFirework = &_LayerTypeFirework;
FXLayerType * const LayerTypeDust = &_LayerTypeDust;

So FXLayerType itself is a constant struct, while as with Obj-C objects we always use pointers to these structs. The implementation creates 3 constant structs and 3 constant pointers to them.

We can now write code such as:

FXLayerType *a, *b;
a = LayerTypeDust;
b = LayerTypeExplosion;

NSLog(@"%d, %d\n", a == b, a->persistence == b->persistence);

Which will output "0, 1" - a and b are different enums (0) but have the same persistence (1). Note here a and b are not constant pointers, only the enum "literals" are defined as constants.

As written this has the disadvantage that you cannot switch on an enum value. However if that is needed just add a second field, say tag, and init it with a unique value using a real enum, say FXLayerStyleTag. You can also remove the indirection if you are happy to always compare tags (e.g. a.tag ==b.tag`). This gives you:

FXLayerType.h:

typedef enum { ExplosionTag, FireworkTag, DustTag } FXLayerTypeTag;
typedef struct { FXLayerTypeTag tag; int persistence; } FXLayerType;

extern const FXLayerType LayerTypeExplosion;
extern const FXLayerType LayerTypeFirework;
extern const FXLayerType LayerTypeDust;

FXLayerType.m:

#import "FXLayerType.h"

const FXLayerType LayerTypeExplosion = { ExplosionTag, 3 };
const FXLayerType LayerTypeFirework = { FireworkTag, 6 };
const FXLayerType LayerTypeDust = { DustTag, 3 };

Use:

FXLayerType a, b;
a = LayerTypeDust;
b = LayerTypeExplosion;

NSLog(@"%d, %d\n", a.tag == b.tag, a.persistence == b.persistence);

A difference between the two designs is the first passes around pointers while the second structures, which may be larger. You can combine them, to get switchable pointer-based enums - that is left as an exercise!

Both these designs also have the (dis)advantage that the number of enum "literals" can be extended at any time.

Upvotes: 2

justin
justin

Reputation: 104698

If you just want a primitive container for type and value, consider this approach:

typedef struct FXLayerValue {
  FXLayerType type;
  int value;
} FXLayerValue;

Then again, a class hierarchy may be worth consideration if things become complex or are better handled dynamically. Caveat: if you have a ton of objects to save and/or create, an objc type will be overkill and degrade performance.

Unfortunately, my Java-Fu isn't good enough to know all the lang differences for enums.

Upvotes: 2

Tharabas
Tharabas

Reputation: 3422

Actually you may assign values to the enum keys in C, as they are nothing but ints:

typedef enum {
  LayerTypeExplosion = 3,
  LayerTypeFirework = 6,
  LayerTypeFire = 7,
  LayerTypeFireJet = -1
} FXLayerType;

You may use them then simply as a restricted set of values, to be assigned to a variable of type FXLayerType.

FXLayerType myLayerType = LayerTypeFirework;
NSLog(@"Value of myLayerType = %i", myLayerType);
// =>  "Value of myLayerType = 6"

Upvotes: 1

Josh Rosen
Josh Rosen

Reputation: 1686

This is not 100% equivalent, but this might be an approach you could take in Objective-C. Basically, create several class-level convenience methods to construction the various configuration of FXLayerType.

@interface FXLayerType
{
@private
int persistence;
}

+ (FXLayerType*)fireworkLayerType;
+ (FXLayerType*)explosionLayerType;
+ (FXLayerType*)jetLayerType;

@end

@implementation FXLayerType

+ (FXLayerType*)explosionLayerTypeWithPersistence:(int)persistence
{
   FXLayerType* layerType = [[FXLayerType new] autorelease];
   layerType->persistence = persistence;
   return layerType;
}

+ (FXLayerType*)explosionLayerType
{
   return [self explosionLayerTypeWithPersistence:3];
}

+ (FXLayerType*)fireworkLayerType
{
   return [self explosionLayerTypeWithPersistence:6];
}

+ (FXLayerType*)jetLayerType
{
   return [self explosionLayerTypeWithPersistence:-1];
}

@end

Usage:

FXLayerType* aJetLayerType = [FXLayerType jetLayerType];

Upvotes: 0

Related Questions