Objects

Note: This page is a work in progress.

An object combines a number of properties and methods.

Related topics:

IsObject can be used to determine if a value is an object:

Result := IsObject(expression)

See Built-in Classes for a list of standard object types. There are two fundamental types:

Table of Contents

Basic Usage

Arrays

Create an Array:

MyArray := [Item1, Item2, ..., ItemN]
MyArray := Array(Item1, Item2, ..., ItemN)

Retrieve an item (or array element):

Value := MyArray[Index]

Change the value of an item (Index must be between 1 and Length, or an equivalent reverse index):

MyArray[Index] := Value

Insert one or more items at a given index using the InsertAt method:

MyArray.InsertAt(Index, Value, Value2, ...)

Append one or more items using the Push method:

MyArray.Push(Value, Value2, ...)

Remove an item using the RemoveAt method:

RemovedValue := MyArray.RemoveAt(Index)

Remove the last item using the Pop method:

RemovedValue := MyArray.Pop()

Length returns the number of items in the array. Looping through an array's contents can be done either by index or with a For-loop. For example:

MyArray := ["one", "two", "three"]

; Iterate from 1 to the end of the array:
Loop MyArray.Length
    MsgBox MyArray[A_Index]

; Enumerate the array's contents:
For index, value in MyArray
    MsgBox "Item " index " is '" value "'"
    
; Same thing again:
For value in MyArray
    MsgBox "Item " A_Index " is '" value "'"

Maps (Associative Arrays)

A Map or associative array is an object which contains a collection of unique keys and a collection of values, where each key is associated with one value. Keys can be strings, integers or objects, while values can be of any type. An associative array can be created as follows:

MyMap := Map("KeyA", ValueA, "KeyB", ValueB, ..., "KeyZ", ValueZ)

Retrieve an item, where Key is a variable or expression:

Value := MyMap[Key]

Assign an item:

MyMap[Key] := Value

Remove an item using the Delete method:

RemovedValue := MyMap.Delete(Key)

Enumerating items:

MyMap := Map("ten", 10, "twenty", 20, "thirty", 30)
For key, value in MyMap
    MsgBox key ' = ' value

Objects

An object can have properties and items (such as array elements). Items are accessed using [] as shown in the previous sections. Properties are usually accessed by writing a dot followed by an identifier (just a name). Methods are properties which can be called.

Examples:

Retrieve or set a property literally named Property:

Value := Object.Property
Object.Property := Value

Retrieve or set a property where the name is determined by evaluating an expression or variable:

Value := Object.%Expression%
Object.%Expression% := Value

Call a property/method literally named Method:

ReturnValue := Object.Method(Parameters)

Call a property/method where the name is determined by evaluating an expression or variable:

ReturnValue := Object.%Expression%(Parameters)

Sometimes parameters are accepted when retrieving or assigning properties:

Value := Object.Property[Parameters]
Object.Property[Parameters] := Value

In fact, the array indexing syntax MyArray[Index] actually invokes the __Item property of MyArray, passing Index as a parameter.

Freeing Objects

Scripts do not free objects explicitly. When the last reference to an object is released, the object is freed automatically. A reference stored in a variable is released automatically when that variable is assigned some other value. For example:

obj := {}  ; Creates an object.
obj := ""  ; Releases the last reference, and therefore frees the object.

Similarly, a reference stored in a property or array element is released when that property or array element is assigned some other value or removed from the object.

arr := [{}]  ; Creates an array containing an object.
arr[1] := {}  ; Creates a second object, implicitly freeing the first object.
arr.RemoveAt(1)  ; Removes and frees the second object.

Because all references to an object must be released before the object can be freed, objects containing circular references aren't freed automatically. For instance, if x.child refers to y and y.parent refers to x, clearing x and y is not sufficient since the parent object still contains a reference to the child and vice versa. To resolve this situation, remove the circular reference.

x := {}, y := {}             ; Create two objects.
x.child := y, y.parent := x  ; Create a circular reference.

y.parent := ""               ; The circular reference must be removed before the objects can be freed.
x := "", y := ""             ; Without the above line, this would not free the objects.

For more advanced usage and details, see Reference Counting.

Extended Usage

Arrays of Arrays

Although "multi-dimensional" arrays are not supported, a script can combine multiple arrays or maps. For example:

grid := [[1,2,3],
         [4,5,6],
         [7,8,9]]
MsgBox grid[1][3] ; 3
MsgBox grid[3][2] ; 8

A custom object can implement multi-dimensional support by defining an __Item property. For example:

class Array2D extends Array {
    __new(x, y) {
        this.Length := x * y
        this.Width := x
        this.Height := y
    }
    __Item[x, y] {
        get => super[this.Width * (y-1) + x]
        set => super[this.Width * (y-1) + x] := value
    }
}

grid := Array2D(4, 3)
grid[4, 1] := "#"
grid[3, 2] := "#"
grid[2, 2] := "#"
grid[1, 3] := "#"
gridtext := ""
Loop grid.Height {
    y := A_Index
    Loop grid.Width {
        x := A_Index
        gridtext .= grid[x, y] || "-"
    }
    gridtext .= "`n"
}
MsgBox gridtext

A real script should perform error-checking and override other methods, such as __Enum to support enumeration.

Custom Objects

There are two general ways to create custom objects:

Meta-functions can be used to further control how an object behaves.

Note: Within this section, an object is any instance of the Object class. This section does not apply to COM objects.

Ad Hoc

Properties and methods (callable properties) can generally be added to new objects at any time. For example, an object with one property and one method might be constructed like this:

; Create an object.
thing := {}
; Store a value.
thing.foo := "bar"
; Define a method.
thing.test := thing_test
; Call the method.
thing.test()

thing_test(this) {
    MsgBox this.foo
}

When thing.test() is called, thing is automatically inserted at the beginning of the parameter list. By convention, the function is named by combining the "type" of object and the method name, but this is not a requirement.

In the example above, test could be assigned some other function or value after it is defined, in which case the original function is lost and cannot be called via this property. An alternative is to define a read-only method, as shown below:

thing.DefineProp 'test', {call: thing_test}

See also: DefineProp

Delegation

Objects are prototype-based. That is, any properties not defined in the object itself can instead be defined in the object's base. This is known as inheritance by delegation or differential inheritance, because an object can implement only the parts that make it different, while delegating the rest to its base.

Although a base object is also generally known as a prototype, we use "a class's Prototype" to mean the object upon which every instance of the class is based, and "base" to mean the object upon which an instance is based.

AutoHotkey's object design was influenced primarily by JavaScript and Lua, with a little C#. We use obj.base in place of JavaScript's obj.__proto__ and cls.Prototype in place of JavaScript's func.prototype. (Class objects are used in place of constructor functions.)

An object's base is also used to identify its type or class. For example, x := [] creates an object based on Array.Prototype, which means that the expressions x is Array and x.HasBase(Array.Prototype) are true, and type(x) returns "Array". Each class's Prototype is based on the Prototype of its base class, so x.HasBase(Object.Prototype) is also true.

Any instance of Object or a derived class can be a base object, but an object can only be assigned as the base of an object with the same native type. This is to ensure that built-in methods can always identify the native type of an object, and operate only on objects that have the correct binary structure.

Base objects can be defined two different ways:

A base object can be assigned to the base property of another object, but typically an object's base is set implicitly when it is created.

Creating a Base Object

Any object can be used as the base of any other object which has the same native type. The following example builds on the previous example under Ad Hoc (combine the two before running it):

other := {}
other.base := thing
other.test()

In this case, other inherits foo and test from thing. This inheritance is dynamic, so if thing.foo is modified, the change will be reflected by other.foo. If the script assigns to other.foo, the value is stored in other and any further changes to thing.foo will have no effect on other.foo. When other.test() is called, its this parameter contains a reference to other instead of thing.

Classes

In object-oriented programming, a class is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods). Wikipedia

In more general terms, a class is a set or category of things having some property or attribute in common. In AutoHotkey, a class defines properties to be shared by instances of the class (and methods, which are callable properties). An instance is just an object which inherits properties from the class, and can typically also be identified as belonging to that class (such as with the expression instance is ClassName). Instances are typically created by calling ClassName().

Since Objects are dynamic and prototype-based, each class consists of two parts:

The following shows most of the elements of a class definition:

class ClassName extends BaseClassName
{
    InstanceVar := Expression
    
    static ClassVar := Expression

    class NestedClass
    {
        ...
    }

    Method()
    {
        ...
    }
    
    static Method()
    {
        ...
    }

    Property[Parameters]  ; Use brackets only when parameters are present.
    {
        get {
            return value of property
        }
        set {
            Store or otherwise handle value
        }
    }
    
    ShortProperty
    {
        get => Expression which calculates property value
        set => Expression which stores or otherwise handles value
    }
    
    ShorterProperty => Expression which calculates property value
}

When the script is loaded, this constructs a Class object and stores it in a global constant (read-only variable) with the name ClassName. If extends BaseClassName is present, BaseClassName must be the full name of another class. The full name of each class is stored in ClassName.Prototype.__Class.

Because the class itself is accessed through a variable, the class name cannot be used to both reference the class and create a separate variable (such as to hold an instance of the class) in the same context. For example, box := Box() will not work, because box and Box both resolve to the same thing. Attempting to reassign a top-level (not nested) class in this manner results in a load time error.

Within this documentation, the word "class" on its own usually means a class object constructed with the class keyword.

Class definitions can contain variable declarations, method definitions and nested class definitions.

Instance Variables

An instance variable is one that each instance of the class has its own copy of. They are declared and behave like normal assignments, but the this. prefix is omitted (only directly within the class body):

InstanceVar := Expression

These declarations are evaluated each time a new instance of the class is created with ClassName(), after all base-class declarations are evaluated but before __New is called. This is achieved by automatically creating a method named __Init containing a call to super.__Init() and inserting each declaration into it. Therefore, a single class definition must not contain both an __Init method and an instance variable declaration.

Expression can access other instance variables and methods via this. Global variables may be read, but not assigned. An additional assignment (or use of the reference operator) within the expression will generally create a variable local to the __Init method. For example, x := y := 1 would set this.x and a local variable y (which would be freed once all initializers have been evaluated).

To access an instance variable (even within a method), always specify the target object; for example, this.InstanceVar.

Declarations like x.y := z are also supported, provided that x was previously defined in this class. For example, x := {}, x.y := 42 declares x and also initializes this.x.y.

Static/Class Variables

Static/class variables belong to the class itself, but their values can be inherited by subclasses. They are declared like instance variables, but using the static keyword:

static ClassVar := Expression

These declarations are evaluated only once, when the class is initialized. A static method named __Init is automatically defined for this purpose.

Each declaration acts as a normal property assignment, with the class object as the target. Expression has the same interpretation as for instance variables, except that this refers to the class itself.

To assign to a class variable anywhere else, always specify the class object; for example, ClassName.ClassVar := Value. If a subclass does not own a property by that name, Subclass.ClassVar can also be used to retrieve the value; so if the value is a reference to an object, subclasses will share that object by default. However, Subclass.ClassVar := y would store the value in Subclass, not in ClassName.

Declarations like static x.y := z are also supported, provided that x was previously defined in this class. For example, static x := {}, x.y := 42 declares x and also initializes ClassName.x.y. Because Prototype is implicitly defined in each class, static Prototype.sharedValue := 1 can be used to set values which are dynamically inherited by all instances of the class (until shadowed by a property on the instance itself).

Nested Classes

Nested class definitions allow a class object to be associated with a static/class variable of the outer class instead of a separate global variable. In the example above, class NestedClass constructs a Class object and stores it in ClassName.NestedClass. Subclasses could inherit NestedClass or override it with their own nested class (in which case (WhichClass.NestedClass)() could be used to instantiate whichever class is appropriate).

class NestedClass
{
    ...
}

Nesting a class does not imply any particular relationship to the outer class. The nested class is not instantiated automatically, nor do instances of the nested class have any connection with an instance of the outer class, unless the script explicitly makes that connection.

However, due to the way methods work for Objects, WhichClass.NestedClass() implicitly passes WhichClass as the first parameter, equivalent to WhichClass.NestedClass.Call(WhichClass). Unless static Call() is overridden, this parameter is automatically passed to __New.

Methods

Method definitions look identical to function definitions. Each method definition creates a Func with a hidden first parameter named this, and defines a property which is used to call the method or retrieve its function object.

There are two types of methods:

The method definition below creates a property of the same type as target.DefineProp('Method', {call: funcObj}). By default, target.Method returns funcObj and attempting to assign to target.Method throws an error. These defaults can be overridden by defining a property or calling DefineProp.

Method()
{
    ...
}

Fat arrow syntax can be used to define a single-line method which returns an expression:

Method() => Expression

Super

Inside a method or a property getter/setter, the keyword super can be used in place of this to access the superclass versions of methods or properties which are overridden in a derived class. For example, super.Method() in the class defined above would typically call the version of Method which was defined within BaseClassName. Note:

The keyword super must be followed by one of the following symbols: .[(

super() is equivalent to super.call().

Properties

A property definition creates a dynamic property, which calls a method instead of simply storing or returning a value.

Property[Parameters]
{
    get {
        return property value
    }
    set {
        Store or otherwise handle value
    }
}

Property is simply the name of the property, which will be used to invoke it. For example, obj.Property would call get while obj.Property := value would call set. Within get or set, this refers to the object being invoked. Within set, value contains the value being assigned.

Parameters can be defined by enclosing them in square brackets to the right of the property name, and are passed the same way - but they should be omitted when parameters are not present (see below). Aside from using square brackets, parameters of properties are defined the same way as parameters of methods - optional, ByRef and variadic parameters are supported.

If a property is invoked with parameters but has none defined, parameters are automatically forwarded to the __Item property of the object returned by get. For example, this.Property[x] would have the same effect as (this.Property)[x] or y := this.Property, y[x]. Empty brackets (this.Property[]) always cause the __Item property of Property's value to be invoked, but a variadic call such as this.Property[args*] has this effect only if the number of parameters is non-zero.

Static properties can be defined by preceding the property name with the separate keyword static. In that case, this refers to the class itself or a subclass.

The return value of set is ignored. For example, val := obj.Property := 42 always assigns val := 42 regardless of what the property does, unless it throws an exception or exits the thread.

Each class can define one or both halves of a property. If a class overrides a property, it can use super.Property to access the property defined by its base class. If Get or Set is not defined, it can be inherited from a base object. If Get is undefined, the property can return a value inherited from a base. If Set is undefined in this and all base objects (or is obscured by an inherited value property), attempting to set the property causes an exception to be thrown.

A property definition with both get and set actually creates two separate functions, which do not share local or static variables or nested functions. As with methods, each function has a hidden parameter named this, and set has a second hidden parameter named value. Any explicitly defined parameters come after those.

While a property definition defines the get and set accessor functions for a property in the same way as DefineProp, a method definition defines the call accessor function. Any class may contain a property definition and a method definition with the same name. If a property without a call accessor function (a method) is called, get is invoked with no parameters and the result is then called as a method.

Fat Arrow Properties

Fat arrow syntax can be used to define a property getter or setter which returns an expression:

ShortProperty[Parameters]
{
    get => Expression which calculates property value
    set => Expression which stores or otherwise handles value
}

When defining only a getter, the braces and get can be omitted:

ShorterProperty[Parameters] => Expression which calculates property value

In both cases, the square brackets must be omitted unless parameters are defined.

__Enum Method

__Enum(NumberOfVars)

The __Enum method is called when the object is passed to a for-loop. This method should return an enumerator which will return items contained by the object, such as array elements. If left undefined, the object cannot be passed directly to a for-loop unless it has an enumerator-compatible Call method.

NumberOfVars contains the number of variables passed to the for-loop. If NumberOfVars is 2, the enumerator is expected to assign the key or index of an item to the first parameter and the value to the second parameter. Each key or index should be accepted as a parameter of the __Item property. This enables DBGp-based debuggers to get or set a specific item after listing them by invoking the enumerator.

__Item Property

The __Item property is invoked when the indexing operator (array syntax) is used with the object. In the following example, the property is declared as static so that the indexing operator can be used on the Env class itself. For another example, see Array2D.

class Env {
    static __Item[name] {
        get => EnvGet(name)
        set => EnvSet(name, value)
    }
}

Env["PATH"] .= ";" A_ScriptDir  ; Only affects this script and child processes.
MsgBox Env["PATH"]

__Item is effectively a default property name (if such a property has been defined):

For example:

obj := {}
obj[] := Map()     ; Equivalent to obj.__Item := Map()
obj["base"] := 10
MsgBox obj.base = Object.prototype  ; True
MsgBox obj["base"]                  ; 10

Note: When an explicit property name is combined with empty brackets, as in obj.prop[], it is handled as two separate operations: first retrieve obj.prop, then invoke the default property of the result. This is part of the language syntax, so is not dependent on the object.

Construction and Destruction

Whenever an object is created by the default implementation of ClassName(), the new object's __New method is called in order to allow custom initialization. Any parameters passed to ClassName() are forwarded to __New, so can affect the object's initial content or how it is constructed. When an object is destroyed, __Delete is called. For example:

m1 := GMem(0, 10)
m2 := {base: GMem.Prototype}, m2.__New(0, 30)

; Note: For general memory allocations, use Buffer() instead.
class GMem
{
    __New(aFlags, aSize)
    {
        this.ptr := DllCall("GlobalAlloc", "UInt", aFlags, "Ptr", aSize, "Ptr")
        if !this.ptr
            throw MemoryError()
        MsgBox "New GMem of " aSize " bytes at address " this.ptr "."
    }

    __Delete()
    {
        MsgBox "Delete GMem at address " this.ptr "."
        DllCall("GlobalFree", "Ptr", this.ptr)
    }
}

__Delete is not called for any object which owns a property named "__Class". Prototype objects have this property by default.

If an exception or runtime error is thrown while __Delete is executing and is not handled within __Delete, it acts as though __Delete was called from a new thread. That is, an error dialog is displayed and __Delete returns, but the thread does not exit (unless it was already exiting).

If the script is directly terminated by any means, including the tray menu or ExitApp, any functions which have yet to return do not get the chance to do so. Therefore, any objects referenced by local variables of those functions are not released, so __Delete is not called.

When the script exits, objects contained by global and static variables are released automatically in an arbitrary, implementation-defined order. When __Delete is called during this process, some global or static variables may have already been released, but any references contained by the object itself are still valid. It is therefore best for __Delete to be entirely self-contained, and not rely on any global or static variables.

Class Initialization

Each class is initialized automatically when a reference to the class is evaluated for the first time. For example, if MyClass has not yet been initialized, MyClass.MyProp would cause the class to be initialized before the property is retrieved. Initialization consists of calling two static methods: __Init and __New.

static __Init is defined automatically for every class, and always begins with a reference to the base class if one was specified, to ensure it is initialized. Static/class variables and nested classes are initialized in the order that they were defined, except when a nested class is referenced during initialization of a previous variable or class.

If the class defines or inherits a static __New method, it is called immediately after __Init. It is important to note that __New may be called once for the class in which it is defined and once for each subclass which does not define its own (or which calls super.__New()). This can be used to perform common initialization tasks for each subclass, or modify subclasses in some way before they are used.

If static __New is not intended to act on derived classes, that can be avoided by checking the value of this. In some cases it may be sufficient for the method to delete itself, such as with this.DeleteProp('__New'); however, the first execution of __New might be for a subclass if one is nested in the base class or referenced during initialization of a static/class variable.

A class definition also has the effect of referencing the class. In other words, when execution reaches a class definition during script startup, __Init and __New are called automatically, unless the class was already referenced by the script. However, if execution is prevented from reaching the class definition, such as by return or an infinite loop, the class is initialized only if it is referenced.

Once automatic initialization begins, it will not occur again for the same class. This is generally not a problem unless multiple classes refer to each other. For example, consider the two classes below. When A is initialized first, evaluating B.SharedArray (A1) causes B to be initialized before retrieving and returning the value, but A.SharedValue (A3) is undefined and does not cause initialization of A because it is already in progress. In other words, if A is accessed or initialized first, the order is A1 to A3; otherwise it is B1 to B4:

MsgBox A.SharedArray.Length
MsgBox B.SharedValue

class A {
    static SharedArray := B.SharedArray   ; A1          ; B3
    static SharedValue := 42                            ; B4
}

class B {
    static SharedArray := StrSplit("XYZ") ; A2          ; B1
    static SharedValue := A.SharedValue   ; A3 (Error)  ; B2
}

Meta-Functions

class ClassName {
    __Get(Name, Params)
    __Set(Name, Params, Value)
    __Call(Name, Params)
}
Name

The name of the property or method.

Params

An Array of parameters. This includes only the parameters between () or [], so may be empty. The meta-function is expected to handle cases such as x.y[z] where x.y is undefined.

Value

The value being assigned.

Meta-functions define what happens when an undefined property or method is invoked. For example, if obj.unk has not been assigned a value, it invokes the __Get meta-function. Similarly, obj.unk := value invokes __Set and obj.unk() invokes __Call.

Properties and methods can be defined in the object itself or any of its base objects. In general, for a meta-function to be called for every property, one must avoid defining any properties. Built-in properties such as Base can be overridden with a property definition or DefineProp.

If a meta-function is defined, it must perform whatever default action is required. For example, the following might be expected:

Any callable object can be used as a meta-function by assigning it to the relevant property.

Meta-functions are not called in the following cases:

Dynamic Properties

Property syntax and DefineProp can be used to define properties which compute a value each time they are evaluated, but each property must be defined in advance. By contrast, __Get and __Set can be used to implement properties which are known only at the moment they are invoked.

For example, a "proxy" object could be created which sends requests for properties over the network (or through some other channel). A remote server would send back a response containing the value of the property, and the proxy would return the value to its caller. Even if the name of each property was known in advance, it would not be logical to define each property individually in the proxy class since every property does the same thing (send a network request). Meta-functions receive the property name as a parameter, so are a good solution to this problem.

Primitive Values

Primitive values, such as strings and numbers, cannot have their own properties and methods. However, primitive values support the same kind of delegation as objects. That is, any property or method call on a primitive value is delegated to a predefined prototype object, which is also accessible via the Prototype property of the corresponding class. The following classes relate to primitive values:

Although checking the Type string is generally faster, the type of a value can be tested by checking whether it has a given base. For example, n.HasBase(Number.Prototype) or n is Number is true if n is a pure Integer or Float, but not if n is a numeric string, since String does not derive from Number. By contrast, IsNumber(n) is true if n is a number or a numeric string.

ObjGetBase and the Base property return one of the predefined prototype objects when appropriate.

Note that x is Any would ordinarily be true for any value within AutoHotkey's type hierarchy, but false for COM objects.

Adding Properties and Methods

Properties and methods can be added for all values of a given type by modifying that type's prototype object. However, since a primitive value is not an Object and cannot have its own properties or methods, the primitive prototype objects do not derive from Object.Prototype. In other words, methods such as DefineProp and HasOwnProp are not accessible by default. They can be called indirectly. For example:

DefProp := {}.DefineProp
DefProp( "".base, "Length", { get: StrLen } )
MsgBox A_AhkPath.length " == " StrLen(A_AhkPath)

Although primitive values can inherit value properties from their prototype, an exception is thrown if the script attempts to set a value property on a primitive value. For example:

"".base.test := 1  ; Don't try this at home.
MsgBox "".test  ; 1
"".test := 2  ; Error: Property is read-only.

Although __Set and property setters can be used, they are not useful since primitive values should be considered immutable.

Implementation

Reference-Counting

AutoHotkey uses a basic reference-counting mechanism to automatically free the resources used by an object when it is no longer referenced by the script. Script authors should not invoke this mechanism explicitly, except when dealing directly with unmanaged pointers to objects.

Temporary references returned by functions, methods or operators within an expression are released after evaluation of that expression has completed or been aborted. In the following example, the new GMem object is freed only after MsgBox has returned:

MsgBox DllCall("GlobalSize", "ptr", GMem(0, 20).ptr, "ptr")  ; 20

Note: In this example, .ptr could have been omitted since the Ptr arg type permits objects with a Ptr property. However, the pattern shown above will work even with other property names.

To run code when the last reference to an object is being released, implement the __Delete meta-function.

Known Limitations:

Although memory used by the object is reclaimed by the operating system when the program exits, __Delete will not be called unless all references to the object are freed. This can be important if it frees other resources which are not automatically reclaimed by the operating system, such as temporary files.

Pointers to Objects

In some rare cases it may be necessary to pass an object to external code via DllCall or store it in a binary data structure for later retrieval. An object's address can be retrieved via address := ObjPtr(myObject); however, this effectively makes two references to the object, but the program only knows about the one in myObject. If the last known reference to the object was released, the object would be deleted. Therefore, the script must inform the object that it has gained a reference. This can be done as follows (the two lines below are equivalent):

ObjAddRef(address := ObjPtr(myObject))
address := ObjPtrAddRef(myObject)

The script must also inform the object when it is finished with that reference:

ObjRelease(address)

Generally each new copy of an object's address should be treated as another reference to the object, so the script should call ObjAddRef when it gains a copy and ObjRelease immediately before losing one. For example, whenever an address is copied via something like x := address, ObjAddRef should be called. Similarly, when the script is finished with x (or is about to overwrite x's value), it should call ObjRelease.

To convert an address to a proper reference, use the ObjFromPtr function:

myObject := ObjFromPtr(address)

ObjFromPtr assumes that address is a counted reference, and claims ownership of it. In other words, myObject := "" would cause the reference originally represented by address to be released. After that, address must be considered invalid. To instead make a new reference, use one of the following:

ObjAddRef(address), myObject := ObjFromPtr(address)
myObject := ObjFromPtrAddRef(address)