Understanding the life cycle of an object is a necessary part of programming. DM makes the problem of managing the life of objects very simple by providing an excellent garbage collector. If you are not already familiar with the working of the garbage collector, I strongly suggest you read about it in the DM reference: Garbage Collection
DM's garbage collector operates by keeping count of the number of places an object is referenced, and deleting the object when that count reaches zero. In addition to variable assignment, inclusion in a list, and the other obvious references, there are some not-so-obvious references which add to the count. These include: Assigning a key to a mob, assigning a tag to an object, and assigning an /atom/movable a place on the map.
Despite the great usefulness of the garbage collector, it is not 'smart'; it can't determine if several cross-referenced objects are isolated from the rest of the program. This kind of 'circular referencing' can cause objects to remain in your world until it shuts down, eating up valuable RAM, so it is very important to prevent these kinds of cross references. Cross referencing is often needed in a program, like in a tree structure where each node must know both it's parent and it's descendants, so programmers often have to resort to manually tracking and deleting the entire tree structure while it is being thrown around by the program, often resulting in such structures being copied when it is necessary to pass them as an argument to a function.
To prevent such a situation, other programming languages provide "weak" references, which are ways to refer to an object without the garbage collector counting the reference. Thus a parent node could keep track of it's child nodes via a strong reference, while the child nodes would reference the parent weakly, allowing the entire structure to be garbage collected as soon as the top node was no longer referenced. Though it is rarely used, DM does provide one means of weakly referencing an object.
TopDM doesn't provide an actual weak type of reference; what it does is allow the program to obtain a (strong) reference to an object via the object's internal reference number. The internal reference number is a number (technically, it's a string, but I'll refer to it as a number throughout the rest of this entry) which is unique to an object during it's lifetime. The number arises from the way Dream Seeker stores the object in memory, and is sometimes used to identify an object with a null tag. Think of this number as the object's default tag. The number can be obtained via the \ref text macro (). In the following example, the variable 'reference_number' will be used to store the internal reference number of the client.
client{
var/reference_number
verb/obtain_number(){
reference_number = "\ref[src]"
\\ Note: There is no space between the \ref macro and it's embedded expression.
world << reference_number
}
verb/find_object(){
world << locate(reference_number)
}
}
Simple, eh? Obtaining a strong reference to the object is just as simple as find the reference number. The second proc in the above example (find_object) simply passes the reference number to the locate proc, which returns the client object the number originally referred to. Using this type of weak reference, we can build cross referencing structures which the garbage collector will still be able to delete intelligently.
parent{
var{
list/children // Strong Reference
}
New(){
var/child/child1 = new()
var/child/child2 = new()
attach_child(child1, child2)
}
proc{
attach_child(){
for(var/child/_child in args){
children.Add(_child)
// Adding an object to a list strongly references it.
_child.set_parent(src)
}
}
}
}
child{
var{
parent_refnum // Weak Reference
}
proc{
set_parent(var/parent/_parent){
parent_refnum = "\ref[_parent]"
}
find_parent(){
return locate(parent_refnum)
}
}
}
For ease of use, I prefer to use a 'weak reference' object which takes care of these behaviors for me. This is, perhaps, the newest programming technique I've picked up, and I imagine the structure of the object will change considerably in time. However, here is the /weak object as I am current using it.
proc/weak(what){
return new /weak(what)
}
weak{
var/reference_number
New(what){
reference_number = "\ref[what]"
}
proc{
resolve(){
return locate(reference_number)
}
}
}
Top
The simulated weak references I have described don't work exactly like the weak references of other languages, and are not part of the general syntax of the language. The most immediate shortcoming of the method I've described is the hassle of storing the number as a number (instead of as a reference to an object) and then resolving the number when you want to use the object. A more important issue is the uniqueness of the reference number. The reference number is only guaranteed to be unique for the life of the object. Once the object has been deleted (perhaps by the garbage collector), the number is freed for reuse by Dream Seeker. This means that any object resolving a weak reference to a deleted object can end up with a reference to a newer quasi-random object [I wonder if this is the same way usr works]. The problem is demonstrated in the following example, which outputs 1 (TRUE) when run from my system.
client{
verb/k(){
var/datum/D1 = new()
var/datum/D2 = new()
var/string1 = "\ref[D1]"
var/string2 = "\ref[D2]"
del D1
var/datum/D3 = new()
string2 = "\ref[D3]"
world << (string1 == string2)
}
}
As with everything in the arcana, proceed at your own risk.
Top