Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C-style array of pointers to Objective-C objects under ARC

I have a 2D array of pointers to Objective-C instances to keep track of game objects on a map grid. Now I am transitioning my code to ARC, and Xcode pointed the error. I knew pointers to objects aren't allowed as struct members, but this one caught me (almost) off guard.

I understand the rationale behind the ARC constrains, but:

  1. I can't afford the overhead of objective-C arrays when looking up objects in the grid, and

  2. The objects themselves are already owned by an NSArray ivar defined in the same class that has the C-style grid as an ivar; the c-style array is only a conveniently structured shortcut. Futhermore, when objects are removed from the owning NSArray, I set the corresponding grid slot to NULL.

That is, the 2D array (grid) is just a collection of fast (but dumb) pointers to objects safely retained somewhere else (the NSArray ivar).

Is there a way to get away with this using casts? For example, define and alloc my grid as:

void*** _grid;

instead of

MyMapObjectClass*** _grid

and use (appropriately bridged) casts between void* <-> MyMapObjectClass* when setting or getting the pointers in each slot?

EDIT: So here is how I solved it

I changed the ivar declaration as described above. In addition, when setting an entry of my look-up grid, I did this:

// (Done **Only Once** at map initialization) 
// _objectArray is an instance of NSMutableArray

MyMapObjectClass* mapObject = [[MyMapObjectClass alloc] init];

// ...configure map object, etc...

// Add to Obj-C array:
[_objectArray addObject:mapObject];

// Add pointer to 2D C array:
_grid[i][j] = (__bridge void*)mapObject;

When accessing the object at (x,y), I do the opposite:

MyMapObjectClass* object = (__bridge MyMapObjectClass*) _grid[x][y];

[object performSomeMethod];

// etc...

When removing the object from the map, I do this:

MyMapObjectClass* object = (__bridge MyMapObjectClass*) _grid[x][y];

[_objectArray removeObject:object];

_grid[x][y] = NULL;

Map objects are created once at the beginning of the game, and removed according to game progress. If I need to replace a map object for another, I would do this:

MyMapObjectClass* oldObject = (__bridge MyMapObjectClass*) _grid[x][y];
// (should mark as weak?)

[_objectArray removeObject:oldObject];    
_grid[x][y] = NULL;

MyMapObjectClass* newObject = [[MyMapObjectClass alloc] init];

[_objectArray addObject:newObject];

_grid[x][y] = (__bridge void*)newObject;
like image 401
Nicolas Miari Avatar asked Jun 22 '12 11:06

Nicolas Miari


1 Answers

Circumventing ARC using casts is generally a bad idea. The better way would be to disable ARC for your map.m (or break out just the lookup part into a separate class).Then do manual memory management inside it with retain / release and the C structures you like, as long as you do it correctly it will work fine and you will be able to call it from other classes, avoiding the overhead of nested NSArrays etc..

like image 95
Hampus Nilsson Avatar answered Nov 03 '22 00:11

Hampus Nilsson