How to pass a multidimensional C-array as NSTimer userInfo? - objective-c

I need to call a function in Objective-C every x seconds, but this function needs to receive a 2D int as parameter.
- (void)myTimer:(int[2][3]) field {
//Rest of the code
}
I tried to use the userInfo option to pass this array, but it looks like it only accepts NSObjects.
int Field[2][3];
memset(Field, 0x00, 6 * sizeof(int));
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(myTimer:) userInfo:Field repeats:YES];
//Error is: Implicit conversion of a non-Objective-C pointer type 'int (*)[3]' to 'id _Nullable' is disallowed with ARC
Is there a way to pass this 2D int or do I need to go with NSObjects or maybe a global variable?
Thank you

I don't think it's possible to pass a stack-allocated array to NSTimer (Objective-C is not very friendly to stack-allocated objects in general. You can pass a pointer to such an array, but NSTimer can outlive almost all stacks in the application, and you may end up with a dangling pointer then) so you at least need to make this array static or global.
Then you can take a pointer to it, and wrap it with NSValue like this:
static int data[array_rows][array_columns] = { { 0, 1, 2 }, { 3, 4, 5 } };
NSValue *userInfo = [NSValue valueWithPointer:&data];
Alternatively you can allocate the array in the dynamic memory:
int (*array)[array_columns] = malloc(array_rows * array_columns * sizeof(int));
if (array) {
int val = 0;
for (int i = 0; i < array_rows; ++i) {
for (int j = 0; j < array_columns; ++j) {
array[i][j] = val++;
}
}
}
NSValue *userInfo = [NSValue valueWithPointer:array];
As you already noticed, NSTimer takes Objective-C classes as arguments, so NSValue can be passed as is:
[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(timerAction:)
userInfo:userInfo
repeats:YES];
Be advised that on the target side the array pointer looks somewhat different. For the static variable you need to dereference the pointer value:
- (void)timerAction: (NSTimer *)timer {
NSValue *userInfo = timer.userInfo;
int (*array)[array_columns] = *(int (**)[array_columns])userInfo.pointerValue;
...
And for the dynamic memory array it's already dereferenced:
- (void)timerAction: (NSTimer *)timer {
NSValue *userInfo = timer.userInfo;
int (*array)[array_columns] = userInfo.pointerValue;
...

Related

Why would a function defined in the same class be considered undeclared? How to declare it properly?

I'm getting the error "undeclared identifier" on the commented line:
- (BOOL) isInIntArray:(NSInteger[])array theElem:(int)elem{
int i = 0;
NSInteger sizeOfArray = (sizeof array) / (sizeof array[0]);
while(i < sizeOfArray){
if(array[i] == elem){
return TRUE;
}
i++;
}
return FALSE;
}
- (int)getNextUnusedID{
int i = rand()%34;
while ([isInIntArray:idsUsed theElem:i]) { //here: Use of undeclared identifier 'isInIntArray'
i = rand()%34;
}
return i;
}
I really don't understand why, they are in the same .m file.
Why would that be?
Also, this code:
NSInteger sizeOfArray = (sizeof array) / (sizeof array[0]);
is giving me the warning:
Sizeof on array function will return Sizeof 'NSInteger *' (aka: 'int *') instead of 'NSInteger[]'"
How should I properly determine the size of an array?
It looks like you've missed out self from this line
while ([isInIntArray:idsUsed theElem:i])
This should be:
while ([self isInIntArray:idsUsed theElem:i])
As #CaptainRedmuff pointed out, you are missing the target object in method invocation, that is self.
//[object methodParam:x param:y];
[self isInIntArray:idsUsed theElem:i];
To your second Q. In C language you cannot determine the size of an array. That's why they are not used, since we have objects for this. I recommend you to use these:
NSMutableArray *array = [[NSMutableArray alloc] init]; // to create array
array[0] = #42; // to set value at index, `#` creates objects, in this case NSNumber
[array insertObject:#42 atindex:0]; // equivalent to the above
NSInteger integer = array[0].integerValue; // get the value, call integerMethod to get plain int
integer = [[array objectAtIndex:0] integerValue]; // equivalent to the above
[array containsObject:#42]; // test if given object is in the array
[array indexOfObject:#42]; // get index of object from array, NSNotFound if not found
array.count; // to get the number of objects
Important: These arrays have variable size and they are not limited! But you can access elements only at indexes 0..(n-1) (where n in number of objects) and you can set values only for indexes 0..n.
In other words, you can not do array[3] = #42; for empty array, you need to fill first 3 positions first (indexes 0, 1 and 2).
write this in .h file (declare the function)
- (BOOL) isInIntArray:(NSInteger[])array theElem:(int)elem;
and call the method using following way
while ([self isInIntArray:idsUsed theElem:i]) { //here: Use of undeclared identifier 'isInIntArray'
i = rand()%34;
}

Inserting NSNumber into NSMutableArray

allTiles is an NSMutuableArray declared with a capacity of 9. Next a loop inserts 9 NSNull objects with a method called resetArray. I have a method called addToArray which accepts an NSNumber and index at which to insert the value at.
In another class I have int currentTileValue = [self getTileValue]; which returns a tile value. This value will be both the number into the array as well as the index. Whenever I attempt to add to the array it crashes with no indication of why.
allTiles = [[NSMutableArray alloc] initWithCapacity:9];
[self resetArray:allTiles];
- (void) resetArray:(NSMutableArray*) array {
for (int i = 0; i < 9; i++) {
[array insertObject:[NSNull null] atIndex:i];
}
}
- (void) addToArray:(NSNumber*)value :(int)index {
[allTiles insertObject: value atIndex:index];
}
//Different class
//Here is where I am trying to insert into the array.
[(BoxView *)self.superview addToArray:(NSNumber*)currentTileValue :currentTileValue];
What would be causing this to crash?
You state that currentTitleValue is an int. Given this, you need to change this line:
[(BoxView *)self.superview addToArray:(NSNumber*)currentTileValue :currentTileValue];
to:
[(BoxView *)self.superview addToArray:#(currentTileValue) :currentTileValue];
You can't cast an int to an NSNumber pointer. You need to convert it.
Also, your addToArray method is incorrect. Since you pre-populate the array with 9 NSNull objects, you need to call replaceObjectAtIndex:withObject:, not insertObject:atIndex (this assumes you just want 9 values at all times):
[allTiles replaceObjectAtIndex:index withObject:value];
You should also rename your addToArray:: method to something better:
- (void)addToArray:(NSNumber*)value index:(int)index {

property for ivar that points to two-dimensional array of pointers to NSStrings

I want to create a class that contains a dynamic, two-dimensional c-array of pointers to NSStrings. I know I can simulate a two-dimensional array using an NSArray containing multiple NSArrays, but if possible I'd like to do this using a traditional two-dimensional c-array. ARC won't allow a simple assignment of a pointer to an NSString to an element of a c-array unless you use "__unsafe_unretained":
#interface NumberStringsArray : NSObject
{
#public
NSString * __unsafe_unretained **_array;
}
To avoid memory leaks and to give an object in the class ownership of each NSString assigned to the c-array, I add a pointer to each NSString object to an NSMutableArray. In -(void)dealloc I free the memory acquired to create the two-dimensional c-array.
Here's my question: How do I declare a property based on the _array ivar so that I can refer to the i,j element of the array as "foobar.array[i][j]" rather than "foobar->array[i][j]"?
Later amplification: I did it in a very similar manner to the answerer except for the __bridge stuff. I don't know if that makes a difference. I allocate the two-dimensional array here:
self->_array = (NSString * __unsafe_unretained **)calloc(_columnCount, sizeof(void *));
if (!self->_array)
return nil;
for (UINT16 i = 0; i < _columnCount; i++)
{
self->_array[i] = (NSString * __unsafe_unretained *)calloc(_rowCount, sizeof(void *));
if (!self->_array[i])
{
for (UINT16 a = 0; a < _columnCount; a++)
if (self->_array[a])
free(self->_array[a]);
if (self->_array)
free(self->_array);
return nil;
}
}
I put pointers to the NSString objects into the array using substrings generated from a file of comma-separated values:
NSArray *numbers = [line componentsSeparatedByString: #","];
for (UINT16 i = 0; i < _columnCount; i++)
{
NSString *number = #"";
if (i < [numbers count])
number = [numbers objectAtIndex: i];
//
// save it in owners
//
[self.owners addObject: number];
self->_array[i][j] = number;
}
In -(void)dealloc I free all the memory:
-(void)dealloc
{
for (UINT16 i = 0; i < self.columnCount; i++)
if (self->_array[i])
free(self->_array[i]);
if (self->_array)
free(self->_array);
}
Declare this property:
#property (nonatomic) NSString * __unsafe_unretained **_array;
Then you can allocate the pointers to objects:
_array= (NSString * __unsafe_unretained **) malloc(M*sizeof(CFTypeRef) );
for(NSUInteger i=0; i<M;i++)
{
_array[i]= ((NSString * __unsafe_unretained *) malloc(N*sizeof(CFTypeRef) );
for(NSUInteger j=0; j<N;j++)
{
_array[i][j]= (__bridge NSString*) (__bridge_retained CFTypeRef) [[NSString alloc]initWithCString: "Hello" encoding: NSASCIIStringEncoding];
// I see that you got habit with C so you'll probably like this method
}
}
Then when you don't need it anymore, free the array:
for(NSUInteger i=0; i<M; i++)
{
for(NSUInteger j=0; j<N;j++)
{
CFTypeRef string=(__bridge_transfer CFTypeRef) _array[i][j];
}
free(_array[i]);
}
free(_array);
You can't because you can't declare a concrete object for an Objective-C class. So
NumberStringsArray object;
is not allowed.
You are forced to declare it as
NumberStringsArray *object = [[NumberStringsArray alloc] init.. ];
so you have to access to the ivar through the correct -> operator applied to pointers. Mind that the object.something in Objective-C is just a shorthand for [object something] while in standard C you would use . to access to fields of a concrete struct.
(Note: This addresses the creation/use of the property to access the data, not the way the data should be managed by conventional Objective-C storage management or by ARC. Thinking about that makes my head hurt.)
If you want a read-only C array to "look" like an Objective-C property, declare the property such as #property (readonly, nonatomic) char* myProp; and then, rather than using #synthesize, implement a getter for it along the lines of:
-(char**)myProp {
return myPropPointer;
// Or, if the array is allocated as a part of the instance --
return &myPropArray[0];
}

Access C Array within blocks (variable array count) Objective-C

Blocks are fine but what about writing C arrays?
Given this simplified situation:
CGPoint points[10];
[myArray forEachElementWithBlock:^(int idx) {
points[idx] = CGPointMake(10, 20); // error here
// Cannot refer to declaration with an array type inside block
}];
after searching a while found this possible solution, to put it in a struct:
__block struct {
CGPoint points[100];
} pointStruct;
[myArray forEachElementWithBlock:^(int idx) {
pointStruct.points[idx] = CGPointMake(10, 20);
}];
this would work but there is a little limitation I have to create the c array dynamically:
int count = [str countOccurencesOfString:#";"];
__block struct {
CGPoint points[count]; // error here
// Fields must have a constant size: 'variable length array in structure' extension will never be supported
} pointStruct;
How can I access my CGPoint array within a block?
OR
Is it even possible at all or do I have to rewrite the block method to get the full functionality?
Another simple answer which works for me is the following:
CGPoint points[10], *pointsPtr;
pointsPtr = points;
[myArray forEachElementWithBlock:^(int idx) {
pointsPtr[idx] = CGPointMake(10, 20);
}];
Maybe you can allocate the array on the heap?
// Allocates a plain C array on the heap. The array will have
// [myArray count] items, each sized to fit a CGPoint.
CGPoint *points = calloc([myArray count], sizeof(CGPoint));
// Make sure the allocation succeded, you might want to insert
// some more graceful error handling here.
NSParameterAssert(points != NULL);
// Loop over myArray, doing whatever you want
[myArray forEachElementWithBlock:^(int idx) {
points[idx] = …;
}];
// Free the memory taken by the C array. Of course you might
// want to do something else with the array, otherwise this
// excercise does not make much sense :)
free(points), points = NULL;

Declare Dynamic Array

How can I declare dynamic array? For example:
int k=5;
I want to have an array like below:
int myArray[k];
if i read the question right.. (unlikely at this point)
NSMutableArray *myArray = [[NSMutableArray alloc] initWithCapacity:k];
Sometimes true arrays (not NSArray) are really needed. See for example indexPathWithIndexes:length: in NSIndexPath, it take array of uintegers as parameter. For array allocation you should use the following approach:
NSUInteger *arr = (NSUInteger*)malloc(elementsCount * sizeof(NSUInteger) );
arr[0] = 100;
free(arr);
In Objective-C, the standard way to do this is to use the NSMutableArray class. This is a container that can hold any object (note that int is not an object! You'll have to wrap your integers in NSNumber.) Quick example:
NSMutableArray* someIntegers = [[NSMutableArray alloc] initWithCapacity:1];
[someIntegers addObject:[NSNumber numberWithInt:2]];
//I've added one thing to my array.
[someIntegers addObject:[NSNumber numberWithInt:4]];
//See how I can put more objects in than my capacity allows?
//The array will automatically expand if needed.
//The array now contains 2 (at index 0) and 4 (at index 1)
int secondInteger = [[someIntegers objectAtIndex:1] intValue];
//Retrieving an item. -intValue is needed because I stored it as NSNumber,
//which was necessary, because NSMutableArray holds objects, not primitives.
Well in my book it's ok to use VLAs in Objective-C.
So something like
int foo = 10;
int bar[foo];
is allowed. Of course this is not a dynamic array as in automatically adjusting its size. But if you only need a native array on the stack that's fine.
You can use Objetive-C++.
First rename your class like this: MyClass.mm the ".mm" extension tells Xcode that this clas is a Objetive-C++ class, not a Objetive-C class.
then you can use dynamics C++ arrays like this:
int *pixels = new int[self.view.size.width];
for (int offset = 0; offset = self.view.size.width; offset++) {
pixeles[offset] = rawData[offset];
}
then you can pass "pixels" in a method:
Scan *myScan = [[Scan alloc] initWhithArray:pixels];
the method "initWithScan" is declared like this:
-(id)initWithArray:int[]pixels;
the "initWithScan" implementation is like this:
-(id)initWithScan:int[]pixels {
if (self = [super init]) {
for (int i = 0; i < self.myView.size.width; i++) {
NSLog(#"Pixel: %i", pixels[i];
}
}
return self;
}
I hoppe this was useful.