C-Style 2D Array as ivar - ios

In C, we could do the following to create a 2D array:
int intArray[10][10];
In C99, we could create a VLA:
size_t col = 10;
size_t row = 10;
int array[row][col];
Within a method in Objective-C, I can create a 2D array that hold ids as follows:
id genObjectArray[10][10];
Is it possible to create an 2d array ivar in Objective-C?
The following is what I have tried:
#interface myClass ()
{
id objArray[][];
//This doesn't work, unless I specific size.
//I want to do this, so that I could specific the size later during
//runtime
}
In C, I could do the following and allocate space for a 2D array later within a block scope:
int **array;
int *elements;
I can do the same within Objective-C, too, but the problem arises when I use id or other object types; other words, the following is not valid:
id **array;
id *elements;
Thus, my question is, is it possible to declare a C-style 2D array as ivar that holds ids?
I understand that we could achieve that using normal NS(Mutable)Array; but this just serves for educational purposes.

You can't do this. For a C99 VLA, the space required for the array is allocated at the point the array is declared. For an ivar, the analogous time to do that would be when the object was allocated and initialized, but there's no support in Objective C to do that. You'd need to have a stronger definition of what an object constructor can do (more like Java's constructors than Objective C's initializers).
The closest you can get would be something like this:
#interface myClass () {
id * objArray;
}
-(instancetype)initWithRow:(size_t)row col:(size_t)col {
self = [super init];
if (self) {
objArray = calloc(row * col * sizeof(id));
}
return self;
}
-(void)dealloc {
free(objArray);
}
In that case, you're declaring the ivar as a pointer and managing the storage yourself (and the stride, for a multi-dimensional array).
Obviously, NSArray is better in all possible ways.

Related

Best way to pass CGFloat by reference to another class

In ClassA, I have a CGFloat value x that I want to pass by reference to ClassB such that if I make a change to the CGFloat in ClassA, it will be reflected in the reference to x in ClassB. Also, when I pass it to ClassB, I want to store it as a property.
I've thought about using a CGFloat pointer, but I'm struggling to figure out the proper syntax to make it a property:
#property(nonatomic) CGFloat *x;
And then to dereference it:
self->x
I thought about using NSNumber but there is no way to set the value using NSNumber such that it will update in ClassB. I thought about giving up and making a wrapper class to store the CGFloat, but this seems like overkill.
What is the best pattern to go about doing this?
I thought about giving up and making a wrapper class to store the CGFloat, but this seems like overkill.
The advantage of this approach is safety, you create an object, both classes reference it, and ARC takes care of the memory management.
The class is easy to define, for example:
#interface ABShare1 : NSObject
#property CGFloat x;
#end
#implementation ABShare1
#end
(in a .h & .m file – same for other examples)
A class using this would be something like:
#implementation ClassA
{
ABShare1 *one;
}
...
one = ABShare1.new; // somewhere in initialisation
...
... one.x = 42; ... z = one.x * 24; ...
Note: the above stores the ABShare1 reference in a private instance variable, you can store it in a property if you wish but there is no need to.
You can call a method on another class passing the object, e.g.:
ClassB *myB;
...
[myB using:(ABShare1 *)sharedVariable];
and that other class can keep the reference as long as it requires, memory management is automatic.
I've thought about using a CGFloat pointer
This is the standard C (a subset of Objective-C) way of "passing by reference".
You can store a CGFloat * in a property, all "object" valued properties in Objective-C just store pointers (e.g. #property NSString *name; stores a pointer to an NSString object).
You must create the variable that the CGFloat * references, the equivalent of new or alloc/init in Objective-C. You can use the address of a variable, e.g. something like:
CGFloat actualX;
CGFloat *x = &actualX;
but you have to manually ensure that the referenced variable, actualX, lives at least as long as its pointer, stored in x, is in use – failure to do that results in a dangling pointer.
The other option is to dynamically allocate the storage, the direct equivalent of new, e.g. something like:
CGFloat *x = malloc(sizeof(CGFloat));
However you are now responsible for determining when the storage is no longer required and releasing it (using free()).
The first solution to you is "overkill" – maybe because while you are freed from concerns over memory management you don't get a "variable" but two functions/methods to get/set a value.
The second solution is closest to feeling like a "variable", you just use *sharedVariable rather than sharedVariable. However while the manual memory management required is standard for C programmers, it is not for Objective-C programmers.
A third approach mixes the two building on how structures (struct) in C can be used: to share a collection of variables rather than share each one individually by address, instead define a struct with a member for each variable, allocate one and share its address, something like:
typedef struct ABShare
{ CGFloat x;
CGFloat y;
} ABShare;
ABShare *one = malloc(sizeof(ABShare));
one->x = 42;
one->y = 24;
The above has the same memory management issues as the second solution, but we can convert it to a very close Objective-C equivalent:
#interface ABShare : NSObject
{
#public // required
CGFloat x;
CGFloat y;
}
#end
#implementation ABShare
#end
Note: Objective-C classes are effectively implemented using structs, indeed the first Objective-C compilers actually translated them into C struct code.
Using this is very close to the C:
ABShare *one = ABShare.new;
one->x = 42;
one->y = 24;
Same "variable" look as C but with automatic memory management.
This last scheme is essentially how Objective-C manages sharing variables when a block is created – all the local variables accessed by the block are moved into a dynamically allocated object/struct and the variables then accessed using ->.
Which is best in Objective-C? The first and the third are both "Objective-C" style, the second is usually avoided accept when interacting with C APIs. Of the first and third pick whichever feels "right" semantically, rather than concerns over performance, to you. HTH
[NSMutableData dataWithLength:sizeof(CGFloat)] and cast mutableBytes to CGFloat*
You can implement getter and setter of property #property(nonatomic) CGFloat x -without pointer

Getting a char out of NSarray in Objective-C

I am trying to get a char out of an NSArray by generating a random number. Here is my code
#interface ClassName : NSObject
#property (nonatomic) char letter;
-(id) init;
-(char) changeLetter;
#end
Implementation File:
#import "ClassName.h"
#interface ClassName()
#property (strong, nonatomic) NSArray *alphabet;
#end
#implementation ClassName
-(id) init {
[self alphabet];
return self;
}
-(NSArray *) alphabet
{
if (!_alphabet) _alphabet =
#[#'A', #'B',#'C', #'D',#'E', #'F',
#'G', #'H',#'I', #'J',#'K', #'L',
#'M', #'N',#'O', #'P',#'Q', #'R',
#'S', #'T',#'U', #'V',#'W', #'X',
#'Y', #'Z'];
return _alphabet;
}
-(char) changeLetter
{
//Picks a number between 0-26
int nextLetter = arc4random_uniform(26);
//Changes the value of the bubble
[self setValue: nextLetter];
return [self.alphabet objectAtIndex:nextLetter];
}
#end
The problem I am running into is the return statement coming from the changeLetter. It is telling me "Incompatible pointer to integer conversion returning 'id' from a function with result type 'char'
I have no idea what the problem is. I am new to Objective-C and am already getting very frustrated it already. It seems so hard and complicated compared to Java.
Can someone tell me what I am doing wrong?
Any help is greatly appreciated.
Coming from Java you might be confused as Java supports autoboxing and autounboxing.
In both Java & Objective-C you cannot store non-object types, such as integers and characters, directly in collections, such as arrays. Instead such values are stored as objects which wrap (or box) the basic value.
In Objective-C the common wrapper is NSNumber which is capable of storing integers, floating point numbers, characters and booleans. There is also an NSValue wrapper for storing other basic values.
Java will wrap a basic value as an object, and unwrap an object to produce the basic value, according to the context automatically. In Objective-C you must do this yourself.
Your expression: #'A' et al takes the character literal, 'A', and wraps it as an NSNumber. In Java the equivalent of the # would be inserted automatically.
So your array _alphabet contains instances of NSNumber. When you access an element of the array using:
[self.alphabet objectAtIndex:nextLetter]
the NSNumber instance in the array is returned, you still need to unwrap it to obtain the character and you do this with the charValue method:
[[self.alphabet objectAtIndex:nextLetter] charValue];
Again Java does the equivalent automatically for you.
Note: for the actual task you are doing there are better ways to return a random capital letter, in your code you could even just use 'A' + nextLetter (remember in C characters are just integers...).
HTH

Objective-C NSArray can't be changed when passed to method in different class

I have an NSArray and I need to change the order of the items within it. I have written a method that will determine the new order:
+(NSArray*)sortProxyForms:(NSArray*)arrayOfForms
{
NSArray* sortedForms = [arrayOfForms sortedArrayUsingComparator:^(HPSModelFormProxy* a, HPSModelFormProxy* b) {
return [#(a.ordinal) compare:#(b.ordinal)]; // #(a.ordinal) aka Boxing turns int into NSNumber
}];
arrayOfForms = [sortedForms copy]; // DOES NOT WORK
return sortedOfForms; // WORKS IF ASSIGNED IN THE CALLER
}
So, I can pass the NSArray to be sorted into the method. I call the method like this:
[HPSModelUtilities sortProxyForms:_formProxies];
If I actually try setting arrayOfForms (a reference to _formProxies) within the method then once I have returned from the method then the array is unchanged.
However, if I return the sorted array from the method and assign it to the NSArray in the calling method then the assignment works:
_formProxies = [HPSModelUtilities sortProxyForms:_formProxies]; // _formProxies NSArray is changed
_formProxies is declared in the calling class, and "HPSModelUtilities" is a different class.
How come the NSArray can be changed in the caller, but not changed in the called method, even though it is passed by reference?
When you pass a value into a method it is copied. This is called "pass by value". The arrayOfForms you are passing in is a pointer to an NSArray. This means that the pointer is copied when passed in. Redirecting this pointer to another instance of an NSArray does not change where the original pointer is pointing.
I would rename your method to (NSArray*)sortedArrayFromProxyForms:(NSArray*)proxyForms
If you really want to change where your NSArray reference is pointing in the method. Do it like this.
+ (void)sortProxyForms:(NSArray**)proxyForms {
*proxyForms = sortedForms;
}
You are passing a copy of the array reference (subtly different than passing by reference), but then you are changing where that reference points with this line:
arrayOfForms = [sortedForms copy];
arrayOfForms no longer points to the array instance you passed, but to a different array. You could pass a pointer of pointer, and change where the caller's pointer is pointing, but for what you are doing, I think the reassignment is fine.
If you'd really like here's what your function would look like with pointer of pointer:
+(void)sortProxyForms:(NSArray**)arrayOfForms {
NSArray* sortedForms = [arrayOfForms sortedArrayUsingComparator:^(HPSModelFormProxy* a, HPSModelFormProxy* b) {
return [#(a.ordinal) compare:#(b.ordinal)]; // #(a.ordinal) aka Boxing turns int into NSNumber
}];
*arrayOfForms = [sortedForms copy];
}
but I'll add the caveat that this isn't a pattern you see often in objective-c, so I'd avoid it when there are other alternatives available.
Also note when calling this function you need to add the & to get the extra level of indirection:
[HPSModelUtilities sortProxyForms:&_formProxies];

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:
I can't afford the overhead of objective-C arrays when looking up objects in the grid, and
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;
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..

Xcode / iOS: Simple example of a mutable C-Array as a class instance variable?

For some reason I just cant seem to get my head around the process of creating a C-Array instance variable for a class that can have elements added to it dynamically at runtime.
My goal is to create a class called AEMesh. All AEMesh objects will have a c-array storing the vertexdata for that specific AEMesh's 3D model for use with OpenGL ES (more specifically it's functionality for drawing a model by passing it a simple C-Array of vertexdata).
Initially I was using an NSMutableArray, on the assumption that I could simply pass this array to OpenGL ES, however that isnt the case as the framework requires a C-Array. I got around the issue by essentially creating a C-Array of all of the vertexdata for the current AEMesh when it came time to render that specific mesh, and passing that array to OpenGL ES. Obviously the issue here is performance as I am constantly allocating and deallocating enough memory to hold every 3D model's vertexdata in the app about a dozen times a second.
So, Im not one to want the answer spoon fed to me, but if anyone would be willing to explain to me the standard idiom for giving a class a mutable c-array (some articles Ive read mention using malloc?) I would greatly appreciate it. From the information Ive gathered, using malloc might work, but this isn't creating a standard c-array I can pass in to OpenGL ES, instead its more of a pseudo-c-array that works like a c-array?
Anyways, I will continue to experiment and search the internet but again, if anyone can offer a helping hand I would greatly appreciate it.
Thanks,
- Adam Eisfeld
The idea would just be to add a pointer to an array of AEMesh structures to your class, and then maintain the array as necessary. Following is a little (untested) code that uses malloc() to create such an array and realloc() to resize it. I'm growing the array 10 meshes at a time:
#interface MyClass : NSObject
{
int meshCount;
AEMesh *meshes;
}
#end
#implementation MyClass
-(id)init {
if ((self = [super init])) {
meshCount = 0;
meshes = malloc(sizeof(AEMesh)*10);
}
return self;
}
-(void)addMesh:(AEMesh)mesh {
if (meshCount % 10 = 0) {
meshCount = realloc(sizeof(AEMesh) * (meshCount + 10));
}
if (meshCount != nil) {
meshes[meshCount] = mesh;
meshCount++;
}
}
#end
It might be worthwhile to factor the array management into it's own Objective-C class, much as Brian Coleman's answer uses std::vector to manage the meshes. That way, you could use it for C arrays of any type, not just AEMesh.
From the information Ive gathered, using malloc might work, but this
isn't creating a standard c-array I can pass in to OpenGL ES, instead
its more of a pseudo-c-array that works like a c-array?
A C array is nothing more than a series of objects ("objects" used here in the C sense of contiguous memory, not the OO sense) in memory. You can create one by declaring it on the stack:
int foo[10]; // array of 10 ints
or dynamically on the heap:
int foo[] = malloc(sizeof(int)*10); // array of 10 ints, not on the stack
int *bar = malloc(sizeof(int)*10); // another way to write the same thing
Don't forget to use free() to deallocate any blocks of memory you've created with malloc(), realloc(), calloc(), etc. when you're done with them.
I know it doesn't directly answer your question, but an even easier approach would be to work with an NSMutableArray instance variable until the point where you need to pass it to the API, where you would use getObjects:range: in order to convert it to a C-Array. That way you won't have to deal with "mutable" C-Arrays and save yourself the trouble.
If you're willing to use ObjectiveC++ and stray outside the bounds of C and ObjectiveC, then you can use a std::vector to amortise the cost of resizing the array of vertex data. Here's what things would look like:
include <vector>
include <gl.h>
#interface MyClass {
std::vector<GLfloat> vertexData;
}
-(void) createMyVertexData;
-(void) useMyVertexData;
#end
#implementation
-(void) createMyVertexData {
// Erase all current data from vertexData
vertexData.erase(vertexData.begin(),
std::remove(vertexData.begin(),
vertexData.end());
// The number of vertices in a triangle
std::size_t nVertices = 3;
// The number of coordinates required to specify a vertex (x, y, z)
std::size_t nDimensions = 3;
// Reserve sufficient capacity to store the vertex data
vertexData.reserve(nVertices * nDimensions);
// Add the vertex data to the vector
// First vertex
vertexData.push_back(0);
vertexData.push_back(0);
vertexData.push_back(0);
// And so on
}
-(void) useMyVertexData {
// Get a pointer to the first element in the vertex data array
GLfloat* rawVertexData = &vertexData[0];
// Get the size of the vertex data
std::size_t sizeVertexData = vertexData.size();
// Use the vertex data
}
#end
The neat bit is that vertexData is automatically destroyed along with the instance of MyClass. You don't have to add anything to the dealloc method in MyClass. Remember to define MyClass in a .mm file

Resources