Strange behaviour: yes it's my fault but I can't find the mistake!

boyfarrell

Registered
Hi everybody,

I have create a class called TwoColumns:
Code:
@interface TwoColumns : MyClass
{
	double xColumn[201];
	double yColumn[201];
}
I have now created a second class that inherits from this called ArrayOfTwoColumns:
Code:
@interface ArrayOfTwoColumns : TwoColumns
{
	TwoColumns* holder[5][5][5];
}
When I come to use the ArrayOfTwoColumns class I initialise it as normal:
Code:
ArrayOfTwoColumns *myArrayOfTwoColumns = [[ArrayOfTwoColumns alloc] init];
However, when I look in the debugger this doesn't seem to initialise the values of the instance variables of TwoColumns (xColumn and yColumn) contained within ArrayOfTwoColumns - the're not zero? In order for me to use myArrayOfTwoColumns I have to zero the data manually in the main function. A method doesn't seem to be able to zero the data. It must be done from the main function. Any idea what is going on?

In fact I have simplified my code here by renaming things and cutting stuff out it is slightly more complicated but only in terms of accessing the array elements - the problem boils down to what I have written above. If the above is impossible then the problem must be with 'some of the details'.

Any ideas?

Daniel
 

Mikuro

Crotchety UI Nitpicker
That's normal. As a long-time REALbasic user, this confused the heck out of me and seemed terribly backwards when I starting learning C/Cocoa, but it really only makes sense. If the compiler zeroed all the data of every variable it made, it would cause a big performance hit. Instead, it uses whatever data happens to be in RAM at the moment and relies on you initializing your data before you use it.

The solution is to override the -init method of TwoColumns to loop through the columns and zero them out.
 

boyfarrell

Registered
Great so it's really wasn't me this time!

I was giving the responsibility to zeroing all data to the ArrayOfTwoColumns class is this where I was going wrong? Do I have to give in to TwoColumns it's self? So that everytime one is made it is zeroed?

Cheers, Daniel.
 

Mikuro

Crotchety UI Nitpicker
Right. It's all up to TwoColumns to manage its variables. So the init method of TwoColumns ought to look something like this:
Code:
- (id)init {
	if (self = [super init]) {
		int i;
		for (i=0; i<201; i++) {
			xColumn[i]=0.0;
			yColumn[i]=0.0;
		}
	}
	return self;
}
That way every time a TwoColumns object is created, it'll clean itself up and set all its variables to 0.
 

boyfarrell

Registered
I arrived at the same code.
Code:
self = [super init];
if( self ) 
{
                unsigned int i;
		for (i=0; i<=(201-1); i++) 
                {
			[self setXColumnElement:i To:0.0];
			[self setYColumnElement:i To:0.0];
		}
}
return self;
I found the code for -init from rentzch.com Love-Hate ObjC page. I use method calls to set the xColumn and yColumn rather than doing it directly. I guess this is equavalent to the above. However, I can't check yet because I'm at work - Grrr! :mad: I've been 'research programming' without XCode or my Mac!
 

Viro

Registered
Or you could always use the memset() function, which is standard C and is much faster than a loop :)
 

Squart

Registered
boyfarrell said:
I arrived at the same code.
Code:
self = [super init];
if( self ) 
{
                unsigned int i;
		for (i=0; i<=(201-1); i++) 
                {
			[self setXColumnElement:i To:0.0];
			[self setYColumnElement:i To:0.0];
		}
}
return self;
I found the code for -init from rentzch.com Love-Hate ObjC page. I use method calls to set the xColumn and yColumn rather than doing it directly. I guess this is equavalent to the above. However, I can't check yet because I'm at work - Grrr! :mad: I've been 'research programming' without XCode or my Mac!
Your Code should be working and is (after my personal taste) the nicest way to do so, but I would use at last the direct assignment because you has then no Methode-Calling what brings a (little) performance hit. (What will be better if the loop grows)
 

Viro

Registered
Method calling in Objective-C classes isn't a 'small' performance hit, thanks to all that runtime dynamism. It's definitely higher than a call to memset, which can be inlined by the compiler and thus removing the overhead of the function call.
 

lurk

Mitä?
Just a question, do you still have this as your member variable declaration:
Code:
 TwoColumns* holder[5][5][5];
Because this is a pointer to a three dimensional array of TwoColumns objects, it does not allocate space for them. Unless you do some allocation that I am not seeing you are just scribbling over some random memory as you initialize the objects in this array.

Then again I could have missed something...
 

Squart

Registered
Viro said:
Method calling in Objective-C classes isn't a 'small' performance hit
Well, that's a question of interpretation. If you to run that -(id)init Methode only once it would be only a small performance hit (some milliseconds). But by running it 5*5*5 (=125) times or more often it can rise exponential and can become noticeable.
That's what I meant with "small performane hit".
 

boyfarrell

Registered
lurk said:
Just a question, do you still have this as your member variable declaration:
Code:
 TwoColumns* holder[5][5][5];
Because this is a pointer to a three dimensional array of TwoColumns objects, it does not allocate space for them. Unless you do some allocation that I am not seeing you are just scribbling over some random memory as you initialize the objects in this array.

Then again I could have missed something...
No nothing has changed in the code. I am hoping that by overriding -init I could alloc the space required ... ? You saying this won't work?

Daniel
 

Squart

Registered
Your problem is, that you are working with a pointer (*) and with arrays ([][][]). You can see the arrays in C as one entity. So it would be the same as TwoCoumns* holder[125].
The easiest way to allocate every single object is on the following way:
Code:
int x, y, z;
for (x = 0; x < 5; x++)
 for (y = 0; y < 5; y++)
  for (z = 0; z < 5; z++)
   holder[x][y][z] = [[TwoColumns alloc] init];
 

boyfarrell

Registered
From a programming point of view it would be nice to put the initialisation to zero inside the -init method. Think it will lead to less mistakes because I don't have to remember the special case everytime I want to make an ArrayOfTwoColumns instance. Would you agree Squart?
 

boyfarrell

Registered
Hello again,

No joy! I've overidden the -init method so that it loops through as we have discussed but it doen't make any difference? See the attached screen shot from the debugger.

The really strange thing is it works when I initialise from inside the main function just after I've create the instance? This code gives me a usable object.
Code:
ArrayOfTwoColumns *myAOTC = [[ArrayOfTwoColumns alloc] init];

	TwoColumns* emptyTwoColumns = [[TwoColumns alloc] init];	
	unsigned int i,j,q;
	for (q=0; q <= (201-1); q++)
		for (j=0; j <= (201-1); j++)
			for (i=0; i <= (201-1); i++)
				[myAOTC setArrayOfTwoColumnsPointI:i J:j Q:q To: emptyTwoColumns];
Here is another debugger screen shot!

Is the trick not to manually loop through each element of xColumn and yColumn (the instance variables of TwoColumns) but to supply ArrayOfTwoColumns with what a freshly initialised TwoColumns looks like?
 

Attachments

Top