The three virtues of a programmer: Laziness, Impatience, and Hubris. – Larry Wall
Difference between revisions of "Delegates"
(→Delegates in Default Properties: Pretty sure that this is only for subobjects. At least in my UI Framework I noticed this issue and it works fine in the class itself. Also UE3 only!) |
m (→Delegates and garbage collection) |
||
(2 intermediate revisions by 2 users not shown) | |||
Line 37: | Line 37: | ||
The following issue exists only in the Unreal Engine 3 after the change of DefaultProperties linking. | The following issue exists only in the Unreal Engine 3 after the change of DefaultProperties linking. | ||
− | When assigning delegates of [[subobject]]s, be aware that the call will be executed within the context of DEFAULT__OBJECTNAME. | + | When assigning delegates of [[subobject]]s, be aware that the call will be executed within the context of <code>DEFAULT__OBJECTNAME</code>. One can tell that by verifying its instance name output such as <code>DEFAULT__OBJECTNAME</code>. |
+ | |||
To avoid this: Either assign the delegate at run-time or pass the actual object reference via the delegate's parameters. | To avoid this: Either assign the delegate at run-time or pass the actual object reference via the delegate's parameters. | ||
+ | |||
+ | == Delegates and garbage collection == | ||
+ | When class method is assigned to a delegate, a strong reference is created to the class instance containing that method. | ||
+ | It may prevent that instance from garbage collection. | ||
+ | |||
+ | For example, it's absolutely erroneous to assign method of class referencing (directly or indirectly) current level to a delegate within UI. User interface persists across level changes, and this delegate reference will prevent old level from garbage collection, leading to a crash. | ||
+ | |||
+ | Basically, in order to gain access to current world in UI delegates, you must use <code>GetPlayerOwner()</code> function. | ||
+ | |||
+ | Note that just declaring function static is not enough to prevent it from creating reference. You must actually reference it as static one. Example: | ||
+ | <uscript> | ||
+ | static function MyStaticFunction() { | ||
+ | |||
+ | } | ||
+ | |||
+ | function MyFunction() { | ||
+ | SomeOtherObject.SomeDelegate = MyStaticFunction; // reference will be created, you don't want this | ||
+ | SomeOtherObject.SomeDelegate = class.static.MyStaticFunction; // you want this instead | ||
+ | } | ||
+ | </uscript> | ||
{{navbox unrealscript}} | {{navbox unrealscript}} |
Latest revision as of 05:25, 22 December 2012
A delegate is a special kind of variable/function combination. Delegates were introduced in Unreal Engine 2 and are declared similar to regular functions. The difference is, that instead of the keyword function or event, delegate functions are declared with the keyword delegate. In Unreal Engine 3, delegate functions were accompanied by a delegate variable type, so delegates can now also be stored in structs or arrays and can be passed into or returned by a function.
Contents
Delegate functions[edit]
A delegate function declaration consists of the following parts:
[modifiers] delegate [returntype] delegatename ( [parameter declarations] ) body_or_semicolon
Like regular functions, delegates can have a function body, which will be the default function associated with the delegate. If no body is defined, the delegate declaration must be terminated with a semicolon.
Like for regular function declarations, the return type is optional. Similarly, parameter declarations are the same as for regular functions. When assigning a function to the delegate, the function's parameter and return types must match the delegate's. The compiler will make sure this is the case and otherwise throw an error. Parameter names must always be specified, even if the delegate does not have a body definition.
Most function modifiers can be used for delegate functions, but some of them don't make much sense. For example, delegates can't be static and do not support the Unreal Engine 3 replication modifiers client, server and reliable. Note that most modifiers, like simulated, only apply to the delegate body and will not have any effect while other functions are assigned to the delegate.
Delegate variable type[edit]
The delegate type for variables, parameters and function return types in Unreal Engine 3 looks very similar to a class limiter:
delegate < delegatename >
Here, delegatename is the name of a delegate function, whose prototype (i.e. parameters and return type) will be used for the delegate variable. Only functions with the same parameter and return types as the referenced delegate function may be assigned to the variable.
While it is possible to declare arrays of delegates, the compiler only allows calling referenced functions through non-array variables. The element of a delegate array first needs to be assigned to a non-array variable of the same delegate type, but can then be called like a normal function.
function callRandomDelegate(array<delegate<X> > delegateList) { local delegate<X> selectedDelegate; // select a random delegate from the list selectedDelegate = delegateList[Rand(delegateList.Length)]; // call that delegate selectedDelegate(); } delegate X();
This example also demonstrates, that the closing angle brackets in arrays of delegates need to be separated by whitespace, just like with class limiters.
Delegates in Default Properties[edit]
While the compiler won't complain about any assignment of functions to delegates in the default properties and this is also done in some of Epic's own code, it doesn't seem to work well for normal users. Calling a delegate that got assigned per default properties may lead to a crash of the whole engine with an access violation error.
Assigning delegates in Subobjects[edit]
The following issue exists only in the Unreal Engine 3 after the change of DefaultProperties linking.
When assigning delegates of subobjects, be aware that the call will be executed within the context of DEFAULT__OBJECTNAME
. One can tell that by verifying its instance name output such as DEFAULT__OBJECTNAME
.
To avoid this: Either assign the delegate at run-time or pass the actual object reference via the delegate's parameters.
Delegates and garbage collection[edit]
When class method is assigned to a delegate, a strong reference is created to the class instance containing that method. It may prevent that instance from garbage collection.
For example, it's absolutely erroneous to assign method of class referencing (directly or indirectly) current level to a delegate within UI. User interface persists across level changes, and this delegate reference will prevent old level from garbage collection, leading to a crash.
Basically, in order to gain access to current world in UI delegates, you must use GetPlayerOwner()
function.
Note that just declaring function static is not enough to prevent it from creating reference. You must actually reference it as static one. Example:
static function MyStaticFunction() { } function MyFunction() { SomeOtherObject.SomeDelegate = MyStaticFunction; // reference will be created, you don't want this SomeOtherObject.SomeDelegate = class.static.MyStaticFunction; // you want this instead }
Declarations | Preprocessor • Classes • Interfaces • Cpptext • Constants • Enums • Structs • Variables (Metadata) • Replication block • Operators • Delegates • Functions • States • Defaultproperties (Subobjects) |
---|---|
Types | bool • byte • float • int • name • string • Object • Class • Enums • Structs (Vector ⋅ Rotator ⋅ Quat ⋅ Color) • Static arrays • Dynamic arrays • Delegates • Typecasting |
Literals | Boolean • Float • Integer • Names • Objects (None ⋅ Self) • Vectors • Rotators • Strings |
Flow | GoTo • If • Assert • Return • Stop • Switch • While • Do...Until • For • ForEach • Break • Continue |
Specifiers | Super • Global • Static • Default • Const |
UnrealScript | Syntax • .UC • .UCI • .UPKG • Comments • #directives • Native |