Null Object Access

Monkey Forums/Monkey Programming/Null Object Access

rIKmAN(Posted 2013) [#1]
I have been chasing a "Null Object Access" error in a large project for a couple of hours now, and seem to found the problem.

Here is a small piece of code to show the issue:
Strict

Import mojo

'-------------------
Global mainClass:MainClass

' Main()
Function Main:Int()
	mainClass = New MainClass
	Return True
End

'-------------------

' MainClass
Class MainClass Extends App

	Field image:Image

	Field class1:Class1

	Method OnCreate:Int()
		
		Self.image = LoadImage("icon.png", 1, Image.MidHandle)
		
		Self.class1 = New Class1
		
		SetUpdateRate(60)
		Return True
	End
	
	Method OnUpdate:Int()
		
		Self.class1.Update()
		
		Return True
	End Method
	
	Method OnRender:Int()
		Cls

		class1.Draw()
		
		Return True
	End Method
End

'---------

'Class1
Class Class1
	
	Field baseImage:Image
	Field class2:Class2
	
	Method New()
		
		Self.baseImage = LoadImage("icon.png", 1, Image.MidHandle)
		
		Self.class2 = New Class2
		
	End Method
	
		
	Method Update:Int()
		
'		If KeyHit(KEY_SPACE)
'			Self.class2 = New Class2
'		EndIf
		
		Return True
	End Method
	
	Method Draw:Int()
	
		Self.class2.Draw()
	
'		If class2 <> Null
'			Self.class2.Draw()
'		EndIf
	
		Return True
	End Method
End Class

'-------

' Class2
Class Class2

	Field image:Image
	
	Method New()
	
		Self.image = mainClass.class1.baseImage
		
	End Method
	
		
	Method Update:Int()
		Return True
	End Method
	
	Method Draw:Int()
	
		DrawImage Self.image, 200, 200
	
		Return True
	End Method
End Class

' ----------


.. and HERE is the image used in the above code.

Running that code "as-is" gives me the "Null Object Access" error.

However if you swap around the Draw call in Class1 and comment out the existing Draw, and do the same for the New Class2 lines (in OnUpdate() and New() of Class1) then the code works as expected.

So, it seems as though the New() method (Class1) has to complete (get to the End Method) before Self.baseImage becomes a valid reference to the image loaded in Class1.

Am I correct in thinking this or have I missed something obvious?

Also is there a better way to access the Fields of other classes than the way I am doing it? ie. "mainClass.class1.baseImage"

I am trying to load a single image and re-use it from multiple classes, rather than loading multiple images in the New() method every time I create a new instance of a class.

Thanks.

PS. Just to add, the error reported by Monkey is line 94, which is in Class2's New() method at:
Self.image = mainClass.class1.baseImage
which is called before the "New Class2" call so should be loaded and thus a valid image?

Sorry for the ramble.


ziggy(Posted 2013) [#2]
In my opinion, the class1 and class2 fiels looks terribly worng in therms of OO design.
If a member of a class has to be shared in class instances, it sould not be a field, it should be a global.
How would it look to you to remove the class1 and class2 fields, and just make the image field a global and, just in case it is null on object creation, call the LoadImage command?

EDIT: That said the error is caused by your Main function.
This sencente:
	mainClass = New MainClass

Is causing the error. When this is called, a cascade of New(s) happen BEFORE the result of the xpression New MainClass is given to the mainClass global. But the mainClass global (wich is null while the New MainClass is being executed) is trying to be used by the New Class2 (to set an imagE).


rIKmAN(Posted 2013) [#3]
Hey ziggy, thanks for the reply.

That makes sense after reading your reply, however after some more experimenting, the following code works:
Strict

Import mojo

'-------------------
Global mainClass:MainClass

' Main()
Function Main:Int()
	mainClass = New MainClass
	Return True
End

'-------------------

' mainClass
Class MainClass Extends App
		
	Field baseImage:Image

	Field class1:Class1
	
	' =================================

	Method OnCreate:Int()

		Self.baseImage = LoadImage("icon.png", 1, Image.MidHandle)

		Self.class1 = New Class1
				
		SetUpdateRate(60)
		Return True
	End
	
	' =================================

	Method OnUpdate:Int()
		Return True
	End Method
	
	' =================================

	Method OnRender:Int()
		Cls
		
		Self.class1.Draw()
		
		Return True
	End Method

	' =================================
End

' -------------------------

' class1
Class Class1

	Field image:Image
		
	Method New()
		
		Self.image = mainClass.baseImage

	End
	
	Method Update:Void()
	End
	
	Method Draw:Void()
		DrawImage Self.image, 200,200
	End

End
The image is loaded, then a New() Class1 is created - within which I set the image to point to the image loaded in mainClass.

After your explanation above, this code should also give a Null Object Access as it is doing the exact same thing (trying to access mainClass.baseImage when mainClass is null as it is still being executed) - but it works fine and draws the image.

The only difference is I am using one less class in this example, and calling the LoadImage in mainClass - but the "mainClass = New mainClass" call is still executing like the previous example (and as you say should be null) so should give the same error...?

If I move the "Self.class1 = New Class1" before the LoadImage (in mainClass) then I get a Null Object Access as expected as the image has not been loaded yet.

I am obviously misunderstand something, but I'm not sure what I am missing.

Thanks.


Gerry Quinn(Posted 2013) [#4]
I have done something like this in cases where a window class handles loading graphics, and passes its choice of graphics to buttons etc. Maybe something like that is what the OP is really planning to do.

However I have moved more towards globals myself in this case, although rarely 'naked' globals. I have a globals.monkey source file that contains managers for commonly used images, fonts, sounds etc., so classes that want a "Help" button can call the global function GetButtonIcon:Image( BUTICON_HELP ).


Midimaster(Posted 2013) [#5]
In your first example you try to use the instance

Field class1:Class1


in a moment, when it it not yet created. The field "class1:Class1" will be created, when returned from this New():

Class MainClass Extends App
...
	Method OnCreate:Int()
		Self.class1 = New Class1


but this New() first jumps down to other classes to other New()s. during this process inside the last New()...

Class Class2
...
	Method New()
		Self.image = mainClass.class1.baseImage
	End Method


you call the reference, that is not finally created at this moment.


I know cases, where I also need a construction like this, but I do it this way:

Class MyGame
     field Sub:Class1, img:Image
     OnCreate()
          img=LoadImage(....)
          sub=New Class1
          sub.StartUp
     End
end


Class Class1
     field sub:Class2
     Method New()
          Self.sub = New Class2
     End
     Method StartUp()
          Self.sub.StartUp
     End
end


Class Class2
     field img:Image
     Method StartUp()
          Self.img = MyGame.img
     End
end



Another proposal without "any main class image":

Class MyGame
     field sub:Class1
     OnCreate()
          Local img = LoadImage(....)
          sub = New Class1
          sub.StartUp(img)
     End
End


Class Class1
     field sub:Class2
     Method New()
          sub = New Class2
     End
     Method StartUp(loc:Image)
          sub.StartUp loc
     End
end


Class Class2
     field img:Image
     Method StartUp(loc:Image)
           img = loc
     End
End



rIKmAN(Posted 2013) [#6]
Thanks for the replies guys.

@Gerry
No it's nothing to do with a GUI - I basically have a framework (app) which within that will run different minigames - I am using iEngine from the Ignition Framework to change states between main app <-> minigames.

As such the minigames will be run, and then nulled when complete (and back to the main app) so I don't really want to use Globals as from the Monkey docs it says they are "permanent", and I don't want a load of images hanging around in memory when they might not even be used.

My OOP is getting better all the time, but I am far from an expert!
Have I misunderstood the docs in regards to Globals?


@Midimaster
Thanks for the examples :)
I actually refactored the code last night and ended up going the same route of your second example, passing the image reference into the New() method of the class, and this worked great.

I need to rewrite it this week though as it got a bit messy due to the lateness and my brain slowly going to sleep.

@All
I am still confused as to why my first example fails and the second example works, even though they seem (to me) to be doing the exact same thing in terms of the image assignment?


Gerry Quinn(Posted 2013) [#7]
No, you are right, although another option to consider might be to make the mini-games completely self-contained and let each load its own graphics. You might still like globals for elements common to all or most games, e.g. fonts.

Obviously a global image manager could discard images too if it needs to, and load then on demand rather than at the start. But mostly I think we are talking about elements of organisation here, so do it the way that makes most sense to you.


rIKmAN(Posted 2013) [#8]
Hey Gerry,

I was/am trying to make the minigames self-contained and load it's own graphics, sorry if my previous posts confused matters.

The minigame did in fact work perfectly when I had it running as it's own singleton, this problem happened when I dropped it into my main game, although this is the 5th game I have added and it's the first time this issue has appeared.

I do also use Globals for fonts as like you say they are used throughout the entire game + minigames + menus etc so that makes complete sense, but minigames will be loaded and nulled as needed so I can't really use Globals in that case.

The problem I am having is coming from trying to access the graphics loaded in the minigames main class from other classes to do with that minigame.

I'll try to use pacman as an example to try and show what I mean with a cutdown piece of code...
' I have a Field to hold the PacmanGame from my main app where I create it and 
' use Ignitions iEngine to change state to it.

' minigamePacman = New PacmanGame
' iStart(minigamePacman,60)

Class PacmanGame
    
    Field player:Player    
    
	Field dots:Dot
	Field dotImage:Image

    Method New()
	
	Self.player = New Player
		
	Self.dotImage = LoadImage("dot.png", 1, Image.MidHandle)
	Self.dots = New Dot
		
    End Method

End Class



Class Player

	Field image:Image
	
	Method New()
		
		' I can load the image here as there is only ever 1 player object
		Self.image = LoadImage("player.png", 1, Image.MidHandle)

	End Method
End Class



Class Dot

	Field image:Image
	
	Method New()
	
		' I try to load each dot using the image loaded in PacmanGame
		' as I do not want to load a new image every time I create a 
		' New() dot object - as there will be loads.
		Self.image = minigamePacman.dotImage
		
	End Method
End Class

The above code would give the same Null Object Access as my OP as I am doing the same thing, and minigamePacman would = null while the New() method was still being run.

As per my post above I can fix this by passing the image as a parameter into the New() method of the Dot class, but from your comment on keeping everything "self contained" I am thinking there is a better way to do this or something I am completely missing, or whether passing it like that is normal?

I'm also unsure if it is correct/safe to be accessing Class fields from other Classes in the way I am doing: mainApp.myClass.myObject.myField - although I seem to remember a post somewhere on here mentioning this method so assumed it was OK to use.

Am I misunderstanding OOP or how I should be doing things?

It's more confusing also as my second example in this thread worked fine despite it doing the same as the first example in my OP, so I am a bit puzzled by it all at the minute.

Thanks.


Midimaster(Posted 2013) [#9]
you are doing always the same mistake.... and it is always the same problem of timing:

In Class Dot you use the object "minigamePacman" during the New() Method. But in this moment this object "minigamePacman" is not created!!! It will be created when (after) all New() methods have returned back to the Main() function.

In your code all New() methods happen during the first New() and not after it. A New() creates an object, but the reference is started first in the next line after calling the New():

....
  minigamePacman = New PacmanGame   ' in this moment "minigamePacman" is not alive!!!
' now the next line:
  mingame.x=4 ' in this moment "minigamePacman" is alive!!!
....  

Class PacmanGame
   Method New()
       
       blabla   ' in this moment "minigamePacman" is not alive!!!

   End ' in this moment it starts to live!!!
End


This will happen step by step:

....
step:   command:
-------------------
1.  ----> minigamePacman = New PacmanGame   ' in this moment "minigamePacman" is not alive!!!

2.  --------> Method New()
       
3.  --------> blabla   ' in this moment "minigamePacman" is not alive!!!

4.  --------> End ' in this moment it starts to live!!!

5.  ----> now the next line:

6.  ----> mingame.x=4 ' in this moment "minigamePacman" is alive!!!

...  


What you are trying to do is to refer to "minigamePacman" somewhere between step 2. and 4. but you first can use it after step 5.


rIKmAN(Posted 2013) [#10]
Midimaster, yeah I know this from the first replies, I even said so myself in my last post...

The above code would give the same Null Object Access as my OP as I am doing the same thing, and minigamePacman would = null while the New() method was still being run.

My pacman code was to try and clearly emphasise why I was trying to do what I was doing with some code - it was written for this thread :)

Could you please explain why my second example in this thread (post #3) works fine even though it is doing the same thing as the code in the OP and the pacman example in terms of the New() method not being finished as you clearly described.

Cheers.


Gerry Quinn(Posted 2013) [#11]
If I understand your examples right, I think the difference is that in the second example you are loading the image during OnCreate() instead of New(). When you call OnCreate() the app is already constructed and has a proper address, so you can load an image at one point in the OnCreate() function and access it at a later point.

You could probably set up your MiniGame class so it has a separate initialisation function that you call after it is constructed, similar to App.OnCreate().


Midimaster(Posted 2013) [#12]
that is easy to explain...

the New() of mainClass:MainClass is already finished when OnCreate() is called. Oncreate() is not the same like New()

As I did understand it OnCreate() is a event driven call caused by the operation system and is not the same like a New().

steps:

1.  ---> Main()
2.  ------> Function Main:Int()
3.  ------> mainClass = New MainClass
4.  ------> Return True  ' from this moment object "mainClass" lives!
5.  ------> End
6.  --->  OS
7.  --->  Create()
8.  ------> Method OnCreate()
9.  ------> Self.baseImage = LoadImage("icon.png", 1, Image.MidHandle)
10. ------> Self.class1 = New Class1
11. ---------> Method New()
12. ---------> Self.image = mainClass.baseImage
' no problem because object "mainClass" already lives!


You can say, that OnCreate() is something like my StartUp(). It is a second call of object "mainClass" after a finished New()


rIKmAN(Posted 2013) [#13]
Ahhhhhhhh I didn't realise that, I thought New() was essentially OnCreate() for our own classes outside the main App class - that makes sense now.

One last thing if you would be so kind, as I would like to make sure I am doing things correctly so as not to cause bigger issues down the line, I'm still learning the OOP ways!

Which is the preferred/accepted/better method in terms of OOP for doing what I am trying to do from the methods posted?
ie. Passing the image as a parameter to New() or usig a seperate initialisiation function to load them after New() has completed, or are they both fine depending on what the coder prefers?

Also are there any issues with accessing things from other classes using the mainApp.myClass.myObject.myField way (where mainApp is a Global handle to my main app instance)

So if I have 3 classes inside each other (similar to the examples posted), and I want to access a Field from an object in Class2 whilst in Class3 I would do something like:
' mainApp = Global handle to mainApp instance
' myClass1 = Field inside mainApp
' myClass2 = Field inside Class1
' myObject = Field Inside Class2
' myField  = Field inside myObject (which is a class ie. player/enemy etc)

mainApp.myClass1.myClass2.myObject.myField


Thanks so much for your help!


Midimaster(Posted 2013) [#14]
to be honest.. I don't know!

At the beginning of my "monkey time" I was very careful and did'nt allow me a lot of things, which are technical possible.

Nowadays I code more radical with wild constructions and all variants, that are possible.

I think, as long as you do not have performance problems you need not think about the best of 10 ways. Just do it. As long as you do not work with thousands of objects but only some dozent, you will not get any performance problems.

And if you get performnce problems you will have to start to examine all 10 ways to find out, what is the best on which target.

for mother-related operations like your example "accesing a field in class 2 whilst in Class 3" I would do it this way:

Class2
   Field X%, Y%, bla:Class3
     Method blabla()
          ....
          bla:Class3= New Class3
          Class3.Mother=Self
     .....
End

[code]Class3
   Field Mother:Class2
     Method Draw()
          ....
          DrawText "hello", Mother.X, Mother.Y
     End
End



Gerry Quinn(Posted 2013) [#15]
The purpose of OO is to help you organise your code, keep things together that should be together, and keep things separate that should be separate. If it gets in the way, remember YOU are the boss. [The machines will take over soon enough, so enjoy it while you can, puny human.]

Organise things the way that makes sense to you. Flexible OO languages like Monkey (and most of its targets) support a lot of ways of doing things, so if the language is getting in the way, there is probably a way of doing what you want that can be expressed nicely in the language too.

All the things you want to do are fine in context except you are scattering image references around between classes a little more promiscuously than I would like. For example, I would never give a dot in Pacman its own image, unless it's a super-dot that has all sorts of individual animations. I would have a game window class that draws all the dots. That said, you are using an engine and maybe that is pushing you in certain ways. And you can, of course, have the dot draw itself if that's what you want anyway.


rIKmAN(Posted 2013) [#16]
Thanks for the info and advice both, it's really appreciated.
As I've said I am still finding my way with OOP, and I'm usually the kind of coder who if it works I leave it and move on, but I'm not sure that is the best way to go with OOP as there may be consequences down the road.

I'm not using a framework, all my code is my own apart from the state machine from Ignition which I use to move between self contained parts of my game (Start menu, main game, mini games etc), but other than than its all my own blood sweat and tears lol.

I decided against using Ignition as being new to Monkey I wanted to make sure I knew what was going on under the hood and not have things hidden away behind behind the framework with no real idea of what was really happening.

I don''t regret the choice at all, and although it has been hard work writing it from the ground up means I know everything inside out, but it may not necessarily be the correct way to do things.- hence this thread and my questions. :)