Q: How can I reference information in one part or package from another (different) part or package?
A: Newton 2.0 OS provides the ability for packages to share informations by exporting or importing units. Units are similar to shared libraries in other systems.
A unit provides a collection of NS objects (unit members.) Units are identified by a name, major version number, and minor version number. Any frame part can export or import zero or more units.
A unit must be declared, using DeclareUnit
, before it's used (imported or exported.) See the docs on DeclareUnit
below for details.
To export a unit, call DefineUnit
and specify the NS objects that are exported.
To import from a unit, simply reference its members using UnitReference
(or UR for short.)
Unit Usage Notes
Units can also be used to share objects among parts within a single package. This avoids the need to resort to global variables or similar undesirable techniques.
A part can export multiple units. To achieve some degree of privacy, you can partition your objects into private and public units. Privacy is achieved by not providing the declaration for a unit.
References to units are resolved dynamically whenever a package is activated or deactivated. For example, a package can be loaded before the package providing the units it imports is loaded. There will be no problem as long as the provider is loaded prior to actually using the imported members.
Conversely, it's possible for the provider to be deactived while its units are in use. The part frame methods, RemovalApproval and ImportDisabled, provide a way to deal with this situation.
Robust code should ensure that the units it imports are available before attempting to use their members. It should also gracefully handle the situation of units being removed while in use. See the DTS sample "MooUnit" for an example.
Unit Build-Time Functions
These functions are available in NTK at build-time only:
DeclareUnit(unitName, majorVersion, minorVersion, memberIndexes)
unitName
- symbol - name of the unit
majorVersion
- integer - major version number of the unit
minorVersion
- integer - minor version number of the unit
memberIndexes
- frame - unit member name/index pairs (slot/value)
return value - unspecified
A unit must be declared by DeclareUnit
before it's used (imported or exported.) The declaration maps the member names to their indexes. A typical declaration looks like:
DeclareUnit('|FastFourierTransforms:MathMagiks|, 1, 0, {
ProtoGraph: 0,
ProtoDataSet: 1,
});
Typically, the declarations for a unit are provided in a file, such as "FastFourierTransforms.unit", that is added to an NTK project (similar to .h
files in C.)
When resolving imports, the name and major version specified by the importer and exporter must match exactly. The minor version does not have to match exactly. If there are units differing only in minor version, the one with the largest minor version is used.
Typically, the first version of a unit will have major version 1 and minor version 0. As bug fixes releases are made, the minor version is incremented. If a major (incompatible) change is made, then the major version number is incremented.
Note: When a unit is modified, the indexes of the existing members must remain the same. In other words, adding new members is safe as long as the indexes of the existing members don't change. If you change a member's index it will be incompatible with any existing clients (until they're recompiled with the new declaration.)
DefineUnit(unitName, members)
unitName
- symbol - name of the unit
members
- frame - unit member name/value pairs (slot/value)
return value - unspecified
DefineUnit
exports a unit and specifies the value of each member. Immediates and symbols are not allowed as member values. A typical definition looks like:
DefineUnit('|FastFourierTransforms:MathMagiks|, {
ProtoGraph: GetLayout("foo.layout"),
ProtoDataSet: { ... },
});
A unit must be declared before it's defined. The declaration used when exporting a unit with n
members must contain n
slots with indexes 0..n-1
. The definition must specify a value for every declared member (this is important.)
UnitReference(unitName, memberName)
or
UR(unitName, memberName)
unitName
- symbol - name of a unit
memberName
- symbol - name of a member of unit
return value - a reference to the specified member
To use a unit member call UnitReference
(UR
for short) with the unit and member name.
The unit name 'ROM
can be used to refer to obects in the base ROM. For example:
UR('ROM, 'ProtoLabelInputLine)
.
Note: references to objects in the base ROM are sometimes called "magic pointers" and have traditionally been provided in NTK by constants like ProtoLabelInputLine
or ROM_SystemSoupName
.
In Newton 2.0 OS, there may also be packages in the ROM. These ROM packages may provide units. Their members are referenced just like any other unit, using UR
, the unitName, and the memberName. This is the mechanism by which licensees can provide product-specific functionality.
AliasUnit(alias, unitName)
alias
- symbol - alternate name for unit
unitName
- symbol - name of a unit
return value - unspecified
AliasUnit
provides a way to specify an alternate name for a unit. Since unit names must be unique, they tend to be long and cumbersome. For example:
AliasUnit('FFT, '|FastFourierTransforms:MathMagiks|);
...so that you could write:
local data := UR('FFT, 'ProtoDataSet):New(points);
...instead of:
local data := UR('|FastFourierTransforms:MathMagiks|,
'ProtoDataSet):New(points);
AliasUnitSubset(alias, unitName, memberNames)
alias
- symbol - alternate name for unit
unitName
- symbol - name of a unit
memberNames
- array of symbols - list of unit member names
return value - unspecified
AliasUnitSubset
is similar to AliasUnit
, except that it additionally specifies a subset of the units members which can be used. This helps restrict code to using only certain members of a unit.
Unit Part Frame Methods
These methods can optionally be defined in a part frame to handle units becoming unavailable.
RemovalApproval(unitName, majorVersion, minorVersion)
unitName
- symbol - name of the unit
majorVersion
- integer - major version number of the unit
minorVersion
- integer - minor version number of the unit
return value - nil
or string
This message is sent to a part frame when an imported unit is about to be deactivated. It may a return a string to be shown to the user as a warning about the consequences of deactivating the package in use. For example:
"This operation will cause your connection to fooWorld to be dropped."
Note: do not assume that the user is removing the package. Other operations such as moving a package between stores also cause package deactivation.
This message is only a warning. The user may decide to proceed and suffer the consequences. If the user proceeds, the ImportDisabled
message (see below) will be sent.
If the removing the unit is not a problem (for example, your application is closed), then RemovalApproval
can return nil
and the user will not be bothered.
ImportDisabled(unitName, majorVersion, minorVersion)
unitName
- symbol - name of the unit
majorVersion
- integer - major version number of the unit
minorVersion
- integer - minor version number of the unit
return value - unspecified
This message is sent to a part frame after an imported unit has been deactivated. The part should deal with the situation as gracefully as possible. For example, use alternative data or put up a Notify and/or close your application.
Unit-Related Glue Functions
These functions are available in the Newton 2.0 Platform file.
MissingImports(pkgRef)
return value - nil
or an array of frames (see below)
glue name - kMissingImportsFunc
MissingImports
lists the units used by the specified package that are not currently available. MissingImports
returns either nil, indicating there are no missing units, or an an array of frames of the form:
{
name: symbol - name of unit desired
major: integer - major version number
minor: integer - minor version number
<other slots undocumented>
}