The commands that tell a Schedule to add actions usually have notation like like M(someMethodName) at the end. M() is a macro used in Swarm to mean that the selector for the message "step" is returned. Selector, or SEL, is a variable type in Objective C which refers to the abstract name used in the compiler to refer to a method, in this case step. M is short for message (or method) and was "created to save the time of typing @selector(myMethod)," in the words of Nelson Minar. Many of the methods available in the Swarm library want input in the form of a selector, an symbolic reference to a method name, and the M() notation is one shorthand way of giving it what it wants.
Some of the methods in the Swarm library will also want a list of parameters that go with the selector. Suppose, for example, you have a psychologist agent that has this method:
- dealWithProblemBetween:anObject And: (id) anotherObject; |
Presumably, you have some code in which there are objects, perhaps named bill and susan, and when you are not needing the Swarm libraries for anything, you just tell your psychologist agent to carry out that method with a command such as:
[yourShrinksName dealWithProblemBetween: bill And: susan]; |
The name of this method is dealWithProblemBetween:And: and its input variables are two objects.
Now suppose you have a whole list of psychologists, and that you want each one of them to deal with the problem between bill and susan. Furthermore, you want them to do it over and over again. To do that, you need Swarm to schedule the job, so you run into that selector problem again. Notice in the Swarm documentation that the Schedule protocol can respond to a method called createActionForEach, which has a prototype like this:
- at: (timeval_t)tVal createActionForEach: target message: (SEL)aSel : arg1 : arg2 |
At the end of this definition, we see this method wants to be given a selector, and then the two arguments that go with it. We know we can grab the selector of the command we want with M(dealWithProblemBetween:And:), so when we tell the schedule object to make each psychologist look into the bill and susan problem, we need a command like this:
[modelSchedule at: 0 createActionForEach: listOfShrinks message: M(dealWithProblemBetween:And:):bill:susan]; |
Admittedly, this notation seems ungainly, but it works.
It is a difficult understand the reason why the selector is needed in the first place. If one is not well versed in Objective C, it may be best just to follow the form of the examples provided with Swarm and not worry about the M() until it is absolutely necessary. [1]
You can go look in the Swarm libraries to see many examples to show why selectors are so vital. Just by coincidence, we happened to be looking at the Object2dDisplay.m file, where there is a particularly clear example of how these selectors come into play. The Object2dDisplay's display method is often scheduled in the observer swarm level of projects that draw on ZoomRaster grids. In order to make this possible, the selector is required.
When an instance of Object2dDisplay is created, one of the first thing the user does it tell that object what the display message for its members is. The Object2dDisplay is passed a selector by the "setDisplayMessage" method.. This bit of code is from SwarmSugarScape's ObserverSwarm.m file.
agentDisplay = [Object2dDisplay createBegin: [self getZone]]; [agentDisplay setDisplayWidget: worldRaster]; [agentDisplay setDiscrete2dToDisplay: [sugarSpace getAgentGrid]]; [agentDisplay setObjectCollection: [modelSwarm getAgentList]]; [agentDisplay setDisplayMessage: M(drawSelfOn:)]; // note the draw method passed as selector agentDisplay = [agentDisplay createEnd]; |
The Object2dDisplay is told which widget it is addressed to setDisplayWidget and which agent list ([modelSwarm getAgentList]). Note how the object agentDisplay is told to set inside it the value of the selector M(drawSelfOn:). It does not ask for the additional information of the input variables that would ordinarly follow drawSelfOn:. It only wants the selector.
Each item in the list of agents, which is retrieved by the command [modelSwarm getAgentList], has the method drawSelfOn:. Here is the method drawSelfOn:, which can be found in SugarAgent.m:
- drawSelfOn: (id < Raster > )r { [r drawPointX: x Y: y Color: 100]; return; } |
If the agent gets the message drawSelfOn:r, then the agent in turn tells the object r to use its drawPointX:Y:Color: method to put the agent on the picture.
The importance of the selector becomes apparent after a study of the file Object2dDisplay.m in the Swarm space library. In Object2dDisplay.m, we find this method:
- setDisplayMessage: (SEL)s { displayMessage = s; return self; } |
This takes the selector and puts its value into an instance variable called displayMessage. The other set methods in Object2dDisplay have already set the variable objectCollection and displayWidget. So, floating around inside the Object2dDisplay instance, are instance variables that can be put to use in scheduling the actions.
When the display method of Object2dDisplay gets scheduled by the ObserverSwarm, this method from Object2dDisplay.m is called:
- display { [...some irrelevant lines omitted...] // if we have a collection to display, just use that. [objectCollection forEach: displayMessage: displayWidget]; } |
The forEach: method in the Swarm library takes a selector as its first argument, and any parameters needed by the selector follow, separated by semicolons. So, in this example, the displayMessage variable has been set as drawSelfOn and the displayWidget has been set as the worldRaster. So when the display method executes, it passes to each object in the list a message that tells it to draw itself on the worldRaster.
Almost all uses of the selector type in Swarm allow a variable number of arguments. It is important to note, however, that these arguments are generally required to be objects. We would have some trouble if the arguments were floating point values, for example. When such a case arises, one if usually forced to write "wrapper" objects around floats in order to pass them to the Swarm library. For example, consider a change in the problem faced by the hypothetical psychologists discussed above. Suppose instead of dealing with bill and susan, they are instructed instead to set some variables inside themselves, such as idealTemperature or setLengthOfFeelers (these are buggish psychologists, say). The method in the psychologist class might have this interface:
- setTemperature: (float)temp And: (float)feeler; |
Now, if you wanted the Swarm to schedule this setTemperature:And: method to happen every time step, perhaps to "reinitialize" the objects to a "fresh" state, then you would be in a world of hurt. If you need the temperature to be set at 37.3 and the feeler to be 54.1, you would be tempted to write this, but you would be making a big mistake:
[modelSchedule at: 0 createActionForEach: listOfShrinks message: M(setTemperature:And):37.3:54.1]; |
The createActionForEach: method is looking for something like SELECTOR:id:id at the end, but this command instead gives it SELECTOR:float:float.
When you need to pass float values in this way, you may have to redesign your methods so that they can take objects. For example, you might make a new kind of object to hold the values of those floating point numbers. This new class is often called a "wrapper" class. If that new class, call it the ParameterHolder for discussion, is able to respond to methods like getTemp and getFeelr, then this problem could be tackled by rewriting the setTemperature:And: method into something like:
- setParameters: holdingObject; |
If you have an instance of ParameterHolder, called aHolder for short, then the psychologist can be told to setParameters by a command like this:
[aShrink setParameters: aHolder]; |
Presumably, inside the setParameters method there are actions that get the values from the aHolder which is passed in, as necessary.
If you need to schedule a whole list of psychologists to reset themselves, the schedule command could be written as:
[modelSchedule at: 0 createActionForEach: listOfShrinks message: M(setParameters):aHolder]; |
[1] | On the off chance that you have reached a point of necessity, and that is why you are reading this guide, consider this explanation of the problem. Many jobs happen inside the swarm library. If you want each member of a certain list to receive a message every time period, you need to give Swarm a way to keep track of the members and the message. Since the objects at which you want the messages aimed already exist and are objects, it is quite straightford to pass a Swarm object that object's name. Passing a Swarm object a method name is, however, more difficult. You need to give the Swarm object something symbolic if it is to receive and remember it. You wouldn't want the Swarm library to be built around the passing of character strings, right? (Well, maybe you would, but pretend your answer is no!) If you pass the selector, you are passing a variable type that the Swarm libraries can remember and use when they need it. |