Change enum values at runtime? - objective-c

Is there a way to assign values to enums during runtime in objective c?
I have several enums and want each of the enum to have certain value.
The values could be read from a xml file. Is there a way to do this?

Unfortunatley, #Binyamin is correct, you cannot do this with an enum. For this reason, I usually do the following in my projects:
// in .h
typedef int MyEnum;
struct {
MyEnum value1;
MyEnum value2;
MyEnum value3;
} MyEnumValues;
// in .m
__attribute__((constructor))
static void initMyEnum()
{
MyEnumValues.value1 = 10;
MyEnumValues.value2 = 75;
MyEnumValues.value3 = 46;
}
This also has the advantage of being able to iterate through the values, which is not possible with a normal enum:
int count = sizeof(MyEnumValues) / sizeof(MyEnum);
MyEnum *values = (MyEnum *) &MyEnumValues;
for (int i = 0; i < count; i++)
{
printf("Value %i is: %i\n", i, values[i]);
}
All in all, this is my preferred way to do enums in C.

No, enums information is erased at compile time.

Related

Can I retrieve the list of all items in an enumeration/type?

I was wondering, is it possible to retrieve a list of all items in an enumeration?
For example I defined:
typedef enum YouJustUnlocked {
Unlocked_A,
Unlocked_B,
//etc... many more
Unlocked_Nothing,
}YouJustUnlocked;
Is there a way to retrieve the list of all "items" (inaccurate word) in the newly defined eumeration type YouJustUnlocked?
Is this supported by Objective-C?
I would like something like:
NSArray * array = [YouJustUnlocked returnAllItemsInWrapperObjectForm];
YouJustUnlocked item = (YouJustUnlocked) [array objectAtIndex:0];
//Assuming that array will contain some sort of wrapper object (like NSInteger)
No, that's not possible without using an equivalent list of descriptions:
YourClass.h:
typedef enum YouJustUnlocked {
Unlocked_A,
Unlocked_B,
//etc... many more
Unlocked_Nothing,
// Add this:
Num_Unlocked
}YouJustUnlocked;
YourClass.m:
static NSString *_youJustUnlockedDesc[Num_Unlocked] = {
#"Unlocked_A",
#"Unlocked_B",
//etc... many more
#"Unlocked_Nothing",
}
#implementation YourClass
+ (NSString *)youHaveUnlockedDesc:(YouJustUnlocked)yju {
NSAssert(yju < Num_Unlocked, #"Out-of-range");
return _youJustUnlockedDesc[yju];
}
...
#end
Notes:
The typedef will be in the header file, while the description array will be within the implementation file.
The Unlocked_Nothing should, conventionally, be the first element of the enum.
IF the values of your enum are contiguous, you could perhaps do something like this:
typedef enum {
eUnlockedNothing = 0,
eUnlockedA, /* inferred that this is 1 */
eUnlockedB,
eUnlockedMax
} YouJustUnlocked;
NSMutableArray *array = [NSMutableArray array];
for (int i = eUnlockedNothing + 1; i < eUnlockedMax; i++)
{
[array addObject:#(eUnlockedNothing + i)];
}
YouJustUnlocked item = (YouJustUnlocked) [[array objectAtIndex:0] intValue]; //enums are default ints.
The result is that you have an array of your enum values that you can access by index. This array doesn't include the eUnlockedNothing or eUnlockedMax values.

Struct containing array of structs in objective C

I have a struct for a tile (all properties are previously-defined enums)
typedef struct {
ShapeType shape;
ColorType color;
PatternType pattern;
IconType icon;
} TileDefinition;
And I want to create another struct that holds 16 tiles, for a "board". Something like this:
typedef struct {
TileDefinition[16]
} BoardDefinition;
And then I want to be able to loop through that, like so:
for(int i=0;i<16;i++) {
TileDefinition tileDef = boardDef[i];
// Do something with tileDef
}
But obviously the setup for the BoardDefinition struct isn't working. How can I set that up? Where should I and shouldn't I use pointers?
Thanks
This is actually a C question.
This bit is fine:
typedef struct {
ShapeType shape;
ColorType color;
PatternType pattern;
IconType icon;
} TileDefinition;
Here, you need to declare BoardDefinition as:
enum { BoardDefinition_NTiles = 16 }; // << declare a constant for your ease of use
typedef struct {
TileDefinition tile[BoardDefinition_NTiles];
} BoardDefinition;
Then to loop through them:
// Given:
BoardDefinition boardDef;
for (size_t i = 0; i < BoardDefinition_NTiles; i++) {
TileDefinition* const tile = &boardDef.tile[i]; // << get a reference to a tile
// member access of the referenced tile:
tile->color.red = 1;
}
The version you wrote creates a copy of the tile:
for (size_t i = 0; i < BoardDefinition_NTiles; i++) {
TileDefinition tile = boardDef.tile[i]; // << creates a copy of the tile, local to the loop body
// member access of the copied tile:
tile.color.red = 1;
}
Option 1: Make BoardDefinition an Objective-C object where it can be set-up in the init method.
Option 2: Use (Objective-)C++ and provide a constructor for struct BoardDefinition.
Option 3: Keep with C and provide an initBoardDefinition(BoardDefinition *boardDef); function.
As trojanfoe suggest, these would be better as Objective-C objects, but if for some reason you don't wont to do that.
You probable want to declare an instance variable of type BoardDefinition in some class for example
#interface MyClass : NSObject
{
BoardDefinition definition;
}
Also you BoardDefinition is wrong it should be something like
typedef struct {
TileDefinition boardDef[16];
} BoardDefinition;
Your loop would then be something like
for(int i=0;i<16;i++) {
TileDefinition tileDef = definition.boardDef[i];
// Do something with tileDef
}
You don't need to use pointers put you could for the TileDefinition in the parent struct something like
typedef struct {
TileDefinition * tileDefinition;
NSUInteger count;
} BoardDefinition;
This way you can have a variable number of TileDefinition. In this case you would have to malloc the tileDefinition soemthing like the following for the class init method.
- (id)init
{
if( (self = [super init]) != nil )
{
definition.count = 16;
definition.tileDefinition = malloc( definition.count * sizeof(*definition.tileDefinition);
}
return self;
}

Convert variable name to string

How can I convert a variable name into a string?
Example:
From this:
NSString *someVariable
int otherVariable
I want to get a NSString with the actual name of the variable, no matter what type it is.
So, for the two variables above I would want to get their names (someVariable, otherVariable).
I managed to solve my problem with this code snippet:
Import the objc runtime
#import <objc/runtime.h>
and you can enumerate the properties with:
- (NSArray *)allProperties
{
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
NSMutableArray *rv = [NSMutableArray array];
unsigned i;
for (i = 0; i < count; i++)
{
objc_property_t property = properties[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
[rv addObject:name];
}
free(properties);
return rv;
}
Hope it helps someone.
Just add " ... " around the variable name. i.e.
"someVariable"
"otherVariable"
to get the string (as a const char*.) If you want an NSString*, use
#"someVariable"
#"otherVariable"
Within a macro, you can use the construction #... to put the quote ... unquote around a macro variable, e.g.
#define MyLog(var) NSLog(#"%s=%#", #var, var)
so that
MyLog(foo);
is expanded to
NSLog(#"%s=%#", "foo", foo);
These are C declarations, and C does not have the introspection capability to give you what you want.
You could probably write a preprocessor macro that would both declare a variable and also declare and initialize a second variable with the name of the first.
But this begs the question of why you need this level of introspection at all.

qsort on an array of pointers to Objective-C objects

I have an array of pointers to Objective-C objects. These objects have a sort key associated with them. I'm trying to use qsort to sort the array of pointers to these objects. However, the first time my comparator is called, the first argument points to the first element in my array, but the second argument points to garbage, giving me an EXC_BAD_ACCESS when I try to access its sort key.
Here is my code (paraphrased):
- (void)foo:(int)numThingies {
Thingie **array;
array = malloc(sizeof(deck[0])*numThingies);
for(int i = 0; i < numThingies; i++) {
array[i] = [[Thingie alloc] initWithSortKey:(float)random()/RAND_MAX];
}
qsort(array[0], numThingies, sizeof(array[0]), thingieCmp);
}
int thingieCmp(const void *a, const void *b) {
const Thingie *ia = (const Thingie *)a;
const Thingie *ib = (const Thingie *)b;
if (ia.sortKey > ib.sortKey) return 1; //ib point to garbage, so ib.sortKey produces the EXC_BAD_ACCESS
else return -1;
}
Any ideas why this is happening?
The problem is two fold:
the first argument to qsort needs to be a pointer to the beginning of the array
the arguments passed to your sort function are actually pointers to the pointers of your data
Consider this working code:
int thingieCmp(const void *a, const void *b) {
NSObject *aO = *(NSObject **)a;
NSObject *bO = *(NSObject **)b;
if (aO.hash > bO.hash) return 1;
else return -1;
}
int main (int argc, const char * argv[]) {
NSObject **array;
array = malloc(sizeof(NSObject*)*20);
for(int i = 0; i < 20; i++) {
array[i] = [NSObject new];
}
qsort(array, 20, sizeof(NSObject*), thingieCmp);
return 0;
}
Note that the comparison function resolves the data pointers by NSObject *aO = *(NSObject **)a and the qsort function takes array as an argument directly.
All of this, though, begs the question of Why bother?
NSArray is very good at holding arrays of objects and is quite conveniently sortable. Performance is excellent in the general case. If performance analysis indicates that it isn't, you can optimize it away relatively easily.
Note, also, that I have been consistent in use of sizeof() -- same type in both places. Also, the const in your original code is not necessary.
I think, one mistake lies right in the line
qsort(array[0], numThingies, sizeof(array[0]), thingieCmp);
Try
qsort(&array[0], numThingies, sizeof(array[0]), thingieCmp);
or even
qsort(array, numThingies, sizeof(array[0]), thingieCmp);
instead. The compiler won't complain here, as qsort is supposed to take a void* and you pass it a Thingy* which can legally be cast to void* without warning, but you really want qsort to operate on the entire array, which has type Thingy**.
Another thing is: the comparator will be called with pointers to the array slots as arguments, so what you get is actually a Thingy**:
int
thingieCmp(void* a, void* b)
{
Thingie *ia = *((Thingie**)a);
Thingie *ib = *((Thingie**)b);
...
}

How do I list all fields of an object in Objective-C?

If I have a class, how can I list all its instance variable names?
eg:
#interface MyClass : NSObject {
int myInt;
NSString* myString;
NSMutableArray* myArray;
}
I would like to get "myInt", "myString", and "myArray". Is there some way to perhaps get an array of names that I can iterate over?
I've tried searching the Objective-C documentation but couldn't find anything (and I'm not sure what this is called either).
As mentioned, you can use the Objective-C runtime API to retrieve the instance variable names:
unsigned int varCount;
Ivar *vars = class_copyIvarList([MyClass class], &varCount);
for (int i = 0; i < varCount; i++) {
Ivar var = vars[i];
const char* name = ivar_getName(var);
const char* typeEncoding = ivar_getTypeEncoding(var);
// do what you wish with the name and type here
}
free(vars);
#import <objc/runtime.h>
NSUInteger count;
Ivar *vars = class_copyIvarList([self class], &count);
for (NSUInteger i=0; i<count; i++) {
Ivar var = vars[i];
NSLog(#"%s %s", ivar_getName(var), ivar_getTypeEncoding(var));
}
free(vars);
Consider gen_bridge_metadata, which is intended for a completely different purpose, but can produce XML files from Objective-C header files.