I looked for similar topics but no luck so far, so here it goes:
In an Objective-C class I declared an int
pointer instance variable to hold an array of int
:
@interface MyList : NSObject {
int index; // A simple int to hold an index reference
NSString *name; // The name of the list
int *bookList; // A pointer to an int array that holds a list of numbers
}
@property (nonatomic) int index;
@property (nonatomic, copy) NSString *name;
@property (nonatomic) int *bookList;
@end
I tested this as follows and all data contained in the instance variables was correctly stored and displayed by the NSLog
statements:
MyList *aList = [[MyList alloc] init];
[aList setIndex:1];
[aList setName:@"ListOne"];
[aList setBookList:(int []){1, 2, 3, 0}];
NSLog(@"Show MyList object's data after object is populated");
NSLog(@"[%d]: %@", aList.index, aList.name);
for (int i = 0; i < 4; i++) {
NSLog(@"bookList[%d] = %d", i, aList.bookList[i]);
}
However, when I send this object as an argument to a method and I try to print the contents of the int
array, I get strange numbers, and the same happens after returning from the method:
-(void)displayMyList:(MyList *)theList {
NSLog(@"Show MyList object's data in displayMyList method");
NSLog(@"[%d]: %@", theList.index, theList.name);
for (int i = 0; i < 4; i++) {
NSLog(@"bookList[%d] = %d", i, theList.bookList[i]);
}
}
I don't know what's wrong with my test code, as the values of the index
and name
instance variables don't get changed when the object is sent to the displayMyList:
method. I debugged step-by-step and the pointer to the int
array points to the same address all the time, so it seems there's a side-effect somewhere that's changing the array's values, or I'm not getting how memory is placed for this type of pointers to int
arrays. Maybe it's just some pointer arithmetic I'm not getting right, because I haven't seen this in any of the iPhone programming books I have.
I wanted to implement the int
array because the math I do with it is really simple and it seemed using an NSArray
was overkill (if this approach doesn't work I can always go with an NSArray
, though).
Your problem is here:
[aList setBookList:(int[]){1,2,3,0}];
The scope of a stack-allocated reference is limited only to the surrounding function, that's why you're getting garbage values in a different scope, because the stack in which it was allocated is no longer in use.
You need to malloc
this list of int
s to push this reference onto the heap, like so:
int sourceList[] = { 1, 2, 3, 0 };
int *bookList = malloc(sizeof sourceList);
memcpy(aList->bookList, sourceList, sizeof sourceList);
Since you called malloc
, you must eventually relinquish the memory occupied by the "array" by calling free
in your -dealloc
method.
As an alternative, you could use an NSArray
of NSNumber
objects, like so:
@property (nonatomic, strong) NSArray *bookList;
//...
#define NUMINT(x) [NSNumber numberWithInt:x]
NSArray *bookList = [NSArray arrayWithObjects:NUMINT(1), NUMINT(2), NUMINT(3), NUMINT(0), nil];
[aList setBookList:bookList];
The array that you're creating when you assign it is local to that method; the memory gets re-used after the method ends, which means that there are basically garbage values ending up there. The pointer itself doesn't change because it's an ivar, and the address it points to doesn't change because you haven't reassigned it. Only the contents of the pointed-to address change.
You'll need to manage the memory yourself if you want it to stick around for the life of your object. This isn't too big a deal, assuming that you won't need to hand off the array to any other object.* There's no need to switch to an NSArray
if you already have logic using int
s.
// Get the memory
// malloc returns a generic C pointer, void *, so the value needs to be
// cast to make the compiler happy.
int * arr = (int *)malloc(LEN_OF_BOOKLIST * sizeof(int));
// Fill in values
//...
// Assign to the ivar
[aList setBookList:arr];
Then you need to free that memory when the object is destroyed:
- (void) dealloc {
free(bookList);
// Clean up other ivars
[super dealloc];
}
In all, this is very like manually handling the memory of any old object. You call malloc
(with an argument stating the amount of memory you want) instead of sending alloc
to the class (which knows how much memory is needed), and use free
instead of release
to relinquish the memory. (Also note that free
, since there is no reference counting, immediately marks the memory for re-use.)
*Which is essentially why reference counting was invented.