How to declare C style arrays in objective-c app? - objective-c

I have two NSArrays, one of CLLocation and one of doubles (encased in objects) that I need to write to C vectors to draw a gradient MKPolyline as defined here (https://github.com/wdanxna/GradientPolyline). I tried to copy some of the code I saw there in preparation to call one of its functions:
points = malloc(sizeof(CLLocationCoordinate2D)*self.run.locations.array.count);
velocity = malloc(sizeof(float)*self.run.locations.array.count);
for(int i = 0; i<self.run.locations.array.count; i++){
points[i] = self.run.locations.array[i];
velocity[i] = [velocities[i] floatValue];
}
Here self.run.locations.array is an array of CLLocations.
Right now I can't even build the project because I have not declared the variables. But where/how do I declare these variables? I don't know C, and the project I am trying to use doesn't seem to include these declarations in a place I can find them.

points is an array of CLLocationCoordinate2D (dynamically allocated) so it should be a pointer to CLLocationCoordinate2D i.e.
CLLocationCoordinate2D *points;
velocity is an array of float so it should be declared as
float *velocity;
Alternatively you can do this
float velocity[];
if you prefer array syntax.

Related

With NSPointerArray, how to iterate over opaque pointers?

I recently discovering these classes like NSMapTable and NSPointerArray, which work like the traditional collections, but also let you store weak references or plain old C pointers. Unfortunately it looks like you can't use the for...in syntax to iterate over non-NSObject pointers. For example:
typedef struct Segment {
CGPoint bottom, top;
} Segment;
...
NSPointerArray *segments = [[NSPointerArray alloc]
initWithOptions:NSPointerFunctionsOpaqueMemory];
...
Segment *s = malloc(sizeof(Segment));
[segments addPointer: s];
...
for (Segment *s in segments) { // nope...
The compiler does not like that last line. The error:
Selector element type 'Segment *' (aka 'struct Segment *') is not a valid object
So, do I need to do this?
for (int i=0, len=segments.count; i<len; i++) {
Segment *seg = [segments pointerAtIndex:i];
...
That's not the end of the world, but I just want to make sure.
(This might be more of theoretical interest.)
NSPointerArray does conform to the NSFastEnumeration protocol, it is only the
for (id object in collection) language construct that cannot be used with arbitrary pointers which
are not Objective-C pointers.
But you can get a whole bunch of pointers from the array by calling the NSFastEnumeration
method countByEnumeratingWithState:objects:count: directly. This is a bit tricky because
that method need not fill the supplied buffer (as explained here: How for in loop works internally - Objective C - Foundation).
Here is a simple example how this would work:
__unsafe_unretained id objs[10];
NSUInteger count = [segments countByEnumeratingWithState:&state
objects:objs count:10];
// Now state.itemsPtr points to an array of pointers:
for (NSUInteger i = 0; i < count; i++) {
Segment *s = (__bridge Segment *)state.itemsPtr[i];
NSLog(#"%p", s);
}
So this does not help to make the code simpler and you probably want to stick with
your explicit loop.
But for large arrays it might improve the performance because the pointers are "fetched"
in batches from the array instead of each pointer separately.
the for (... in ...) syntax won't work in this case because Segment is a struct, not an Objective C object. Your second for loop should work.

Passing array of float pointers into VBO

I'm creating a particle system renderer, the problem is that all my particle positions are encapsulated into classes that integrate them over time and do some other stuff. Instead of copying the values into a separate array to render each frame, I created a point struct like this that I can use to point to all the values once:
typedef struct
{
float *x, *y, *z;
} point;
I then malloc an array of these the same size as my array of particles.
Declaration at the top:
point *points;
Malloc:
points = malloc(sizeof(point) * [particles count]);
I then loop through all the particles and copy the addresses of each position to the corresponding pointer so:
for (int i = 0; i < [particles count]; ++i)
{
points[i].x = &[[particles objectAtIndex:i] getPosition].x;
points[i].y = &[[particles objectAtIndex:i] getPosition].y;
points[i].z = &[[particles objectAtIndex:i] getPosition].z;
}
My get position function just returns a vector struct (float x, y, z).
The first problem I encountered with this is that when I display the values like so:
NSLog(#"%f", *points[0].x);
Instead of reading of the value (say: 0.5), it displays it rounded and negated (so: -1.0).
I then get the data into my VBO like this:
glBufferData(GL_ARRAY_BUFFER, sizeof(point) * [particles count], &points[0], GL_DYNAMIC_DRAW);
I think this is also wrong but can't find any information on how to pass pointers into VBOs.
What you're doing is wrong because in C everything is passed by value, so when you dereference the .x member of the returned struct, you're doing it on a copy of the position struct, which will be popped from the stack if not assigned to any value.
You're design is wrong, you don't need a struct like this one:
typedef struct
{
float *x, *y, *z;
} point;
Rather one like this:
typedef struct
{
float x,y,z;
}
If you still want to assign it to it's original value, in a way that if the original struct's values change, also this struct is changed, then you need a getter who will return a pointer to the struct, and hold a pointer to it:
points[i] = [[particles objectAtIndex:i] getPositionPointer];
This way you're sure that your struct contains the same values of the original struct, but you're violating encapsulation. Which makes me think that maybe you don't really need to hold a pointer to original struct, but if there's a reason to do then I've shown you the way.

NSInteger = NSInteger -1?

I am trying to do a very simple thing but I can't figure out how;
NSInteger * a=10;
a=a-1;
NSlog(#"a=%d",a);
For some reason it's showing a=6.
How can it be?
Your problem is that you've declared the variable a as a pointer.
Most Objective-C variables are pointers, but NSInteger is an exception, because it's just typedef'd to int or long.
Your code should look like this:
NSInteger a=10;
a=a-1;
NSlog(#"a=%d",a);
When you do math on a pointer, you are actually moving the location in memory it points to. For example if the size of an NSInteger is 4 (sizeof(NSInteger) == 4), moving it -1, or in other words, a one structure size back, the pointer gets decreased by 4.
This mechanique is heavily used in C when iterating arrays of structures, e.g.
CGPoint myPoints[4];
CGPoint* point = myPoints; //get the first point
for (NSUInteger i = 0; i < 4; i++) {
CGPoint currentPoint = *point;
point++; //moves to the next point, adding sizeof(CGPoint)
}
This is called pointer arithmetic and you can write it in different ways, e.g. pointer + 1 but also point[1] or 1[point] (the last two are actually equal to *(pointer + 1)).

Objective-c: Adding a custom object to a NSMutableArray

I usually program in java or c++ and I recently started with objective-c. Looking for vectors in objective-c, I found NSMutableArray which seems to be the best option. I'm working on an opengl game and I'm trying to create an NSMutableArray of textured quads for my sprites. Here is the relevant code:
I define textured quads:
typedef struct {
CGPoint geometryVertex;
CGPoint textureVertex;
} TexturedVertex;
typedef struct {
TexturedVertex bl;
TexturedVertex br;
TexturedVertex tl;
TexturedVertex tr;
} TexturedQuad;
I create an array in the interface:
#interface Sprite() {
NSMutableArray *quads;
}
I initiate the array and I create the texturedQuads based on "width" and "height", which are the dimensions of a single sprite, and "self.textureInfo.width" and "self.textureInfo.height", which are the dimensions of the entire sprite sheet:
quads = [NSMutableArray arrayWithCapacity:1];
for(int x = 0; x < self.textureInfo.width/width; x++) {
for(int y = 0; y < self.textureInfo.height/height; y++) {
TexturedQuad q;
q.bl.geometryVertex = CGPointMake(0, 0);
q.br.geometryVertex = CGPointMake(width, 0);
q.tl.geometryVertex = CGPointMake(0, height);
q.tr.geometryVertex = CGPointMake(width, height);
int x0 = (x*width)/self.textureInfo.width;
int x1 = (x*width + width)/self.textureInfo.width;
int y0 = (y*height)/self.textureInfo.height;
int y1 = (y*height + height)/self.textureInfo.height;
q.bl.textureVertex = CGPointMake(x0, y0);
q.br.textureVertex = CGPointMake(x1, y0);
q.tl.textureVertex = CGPointMake(x0, y1);
q.tr.textureVertex = CGPointMake(x1, y1);
//add q to quads
}
}
The problem is I don't know how to add the quad "q" to the array "quads". Simple writing [quads addObject:q] doesn't work because the parameter should be an id not a TexturedQuad. I've seen examples of how to make an id from an int etc, but I don't know how to do it with an object like my TexturedQuad.
The essence of it is that you wrap your C struct in an Obj-C class. The Obj-C class to use is NSValue.
// assume ImaginaryNumber defined:
typedef struct {
float real;
float imaginary;
} ImaginaryNumber;
ImaginaryNumber miNumber;
miNumber.real = 1.1;
miNumber.imaginary = 1.41;
// encode using the type name
NSValue *miValue = [NSValue value: &miNumber withObjCType:#encode(ImaginaryNumber)];
ImaginaryNumber miNumber2;
[miValue getValue:&miNumber2];
See here for more information.
As #Bersaelor pointed out, if you need better performance use pure C or switch to Obj-C++ and use vectors instead of Obj-C objects.
An NSMutableArray takes any NSObject* but not just structs.
If you're serious about programming in Objective-C, take a look at some tutorials.
Furthermore, NSMutableArrays are meant for convenience, if your adding/deleting a lot of objects to that Array, use plain C-stacks.
Especially for your use-case that more low-level approach will get better performance.
Keep in mind, Objective-C(++) is just a superset of C(++), so you can use any C(++) code you are already familiar with.
When I wrote my game tactica for iOS, I switched to C-Code whenever I had to do heavy lifting (i.e. recursive AI-functions that get called hundreds of times per second).

2d arrays of ints in objective-c

I am working with C style 2d arrays of integers.
This is fine in my main class file, and I can pass my arrays by reference to other classes. I have run into issues when I try to retrieve a pointer to the arrays and work with that.
My question is: rather than using C style 2d arrays, is there a better way to do it? maybe a Cocoa class I don't know about? I noticed NSMatrix, but that seems geared for cells, rather than plain ints.
I've got calls all over the place in this format: items[x][y], so a shorthand way of referencing array positions would be great.
Further details:
I set up the arrays as instance variables, and tried to access like this:
-(void) setItems: (int [15][24])items
{
(*pitems)[24] = **items;
}
-(int) getItems
{
return (*pitems)[24];
}
When I tried to retrieve using getItems, I was getting compiler warnings about creating a reference without a cast.
An interesting discussion here: http://www.idevapps.com/forum/archive/index.php/t-244.html
Basically the suggestion was to turn your 2D array into multiple 1D arrays. For Example:
int array[20][8] becomes
int** array = (int*)malloc(20 * sizeof(int*));
unsigned int i = 0;
for (i = 0; i < 20; ++i)
{
array[i] = (int)malloc(8 * sizeof(int));
}
and your method returns int** and variable is of type int**.
This has the advantage that normal 2D array indexing works as expected.
The other option that wasn't suggested in the link was to use a NSMutableArray of type NSMutableArray. This would be slower than standard C arrays, but easier to pass around and reference.
You can only pass around 2D static arrays if you know the exact size of the first dimension:
// OK, even though we don't know the size of the second dimension
- (void) doStuff: (int [15][])array { ... }
...
int array[15][24];
[self doStuff:array];
// ERROR: don't know size of first dimension (second dimension is irrelevant)
- (void) doStuff: (int [][])array { ... }
Of course, then your function only works for one particular array size in the first dimension. If you won't know the size if your first dimension until runtime, you'll have to either flatten your array into a 1D array, or use a dynamically allocated array of pointers to 1D arrays as in KiwiBastard's answer.