8.3. Passing Information Around

Getting the Import Statements Right

In order to send messages to objects from another class, it is necessary not only to use the correct message, but also to import that class's header file into the code. The ObserverSwarm.m file can only tell the HeatbugModelSwarm to run its createBegin method if ObserverSwarm.m includes the header file for the HeatbugModelSwarm. In HeatbugObserverSwarm.m, we find this:

#import "HeatbugModelSwarm.h"

The inclusion in the "m" file is sufficient if no reference to the HeatbugModelSwarm is necessary in the HeatbugObserverSwarm.h file. It may be necessary to move the import statement into the header file (the "h" file), however, if any references to a class are contained in the "h" file. In HeatbugModelSwarm.h, for example, one finds these import statements:

#import "Heatbug.h"
#import "HeatSpace.h"

Since these are included, the variable and method definitions can refer to elements of these classes. The variable list declares a pointer to an object of type HeatSpace:

HeatSpace *heat

and there is a method that has an object of type Heatbug as an argument:

-addHeatbug: (Heatbug *)bug;

Many Swarm programmers have run into the following problem. As we have seen, It is not difficult to have the model swarm level create an object. Through the set methods, various values can be set inside the object by commands in the model swarm. However, the programmer wants the agent to be able to access variables inside the model swarm as the simulation progresses. Suppose the HeatbugModelSwarm has an instance variable called numberOfBugsAlive, and inside HeatbugModelSwarm we define a method getNumberOfBugsAlive that returns that number. Suppose further we want any heatbug to be able to find out how many bugs are alive at any instant. It is tempting to write inside Heatbugs.m something like:

[heatbugModelSwarm getNumberOfBugsAlive];

to access that information. That construction will not work, however, unless we take some special precautions. First, each Heatbug has to be made "aware" of what model swarm it belongs to. Inside Heatbug.h, a variable would have to be defined:

id heatbugModelSwarm;

To set the value of this variable, the Heatbug.m file needs to have a method like this:

- setModelSwarm: (id) nameOfSwarm
heatbugModelSwarm = nameOfSwarm;
return self;

The value of the instance variable heatbugModelSwarm has to be set in the model swarm when other values are set. When the HeatbugModelSwarm is creating bugs, it sets the other values like the ideal temperature and the position, but further it would set itself as the model swarm to which that bug belongs, like so:

aBug = [Bug createBegin: globalZone];
aBug = [aBug createEnd];
[aBug setWorldSizeX: xsize Y: ysize];
[aBug setFoodSpace: foodSpace];
[aBug setX: xPos Y: yPos];
[aBug setIdealTemp: [uniformDblRand getDoubleSample]];
[aBug setModelSwarm: self];

This assures that, inside aBug, the value of the instance variable heatbugModelSwarm is defined.

The final precaution is that the header file HeatbugModelSwarm.h must be imported into Heatbug.m. It is very important that the import statement is added to Heatbug.m, not Heatbug.h. If it is added to Heatbug.h, then the program will not compile because the inclusion causes a circularity: Heatbug.h is included in HeatbugModelSwarm.h, but HeatbugModelSwarm.h is also included in Heatbug.h. Putting the import statement in the "m" file avoids that ciruclarity. And, since the import has to be in the "m" file, the definition of the variable heatbugModelSwarm in Heatbug.h uses the generic type id, rather than a specific type, such as HeatbugModelSwarm.

Many swarm examples are designed to avoid the need to allow objects created by model swarm to also access information directly from it. This is usually done by creating a "space" object that keeps records on the model swarm. Individual agents report their positions to the space and the space calculates any necessary statistics about the swarm. The code for the space object can include get methods that the individual agents can execute when they need information about their environment. This approach has the added advantage that additional methods can be inherited from the general space objects in the Swarm library.