What do all those bits in an ObjectID mean, anyway?
Being able to tell something about an object from its ObjectID is an incredibly handy thing to be able to do; here's a description of the more important bits in an ObjectID:
Given an 32 bit value 0xXYZ00000, you can tell it's an ObjectID or indexical if the high (leftmost) bit is set. In other words, an ObjectID or indexical will always start with a digit from 0x8 through 0xF. The digit represented by X tells you the logical cluster that that object is in. For example, an ObjectID starting with 0x8 tells you that the object is in the system persistent cluster; a 0xB tells you that the object is in the package persistent cluster.
Digit Y gives you more information about the type of ObjectID this is. If it's odd (0x3 or 0x7), that means this ID is a system indexical number. If it's even, that means this ID refers to an object instance. The next bit of this digit is know as the the indirect bit. If this bit is set (Y's value is 0x2 or 0x6), this means that this ID is a reference object. Reference objects are used to refer to objects that did not originate from your context. You'll see instances of these objects when you Import objects from another package. Note that the indirect bit is set for indexicals. The next higher bit is the usable bit. If this bit is set (0x4), it means that the object can be accessed (or usually, is already accessed). When you're executing a class operation and look at the ObjectID for the self parameter, this bit will always be set. This bit is clear if the object is not being used at the time; if you look at some random ObjectList in the system, the ObjectIDs it contains will (should!) all have this bit clear.
Finally, digit Z mostly contains generation bits. If an ObjectID ever gets reused, these bits will start getting set to distinct this instantiation of the object number from an older one.
The rest of the numbers in an ObjectID comprise the actual object number. This is used as an index into a MasterBlock in a cluster. A MasterBlock entry gives an offset into the physical cluster where the object is located. As objects move around in memory, its MasterBlock entry is updated, but the object number stays the same. This is similar to how handles work on the Macintosh.
For a complete description of the bits in an ObjectID, look in the file {MagicDeveloper}Interfaces:FrameworkDefines.asm.h.
What is the +1 that sometimes follow the length of an object in a cluster dump?
The physical size of an object may not be the same as the logical size of an object (to maintain alignment in memory for speed). The +1 is the adjustment.
Is there any way to specify data is private to an object, so it is only accessible to the object itself?
All the fields (data) of an object are accessible to other objects, if its objectID and class definition are known.
However, we consider it "bad form" to access fields of classes other than your own. They should be treated as private, and future tools may provide enforcement for this practice.
My package has an intrinsic function which takes an ObjectID and a Pointer as its parameters.
Sometimes, on calling this function, the application goes into the debugger with the error message
"Using storable ID". What causes this to happen?
An ObjectID has two formats, its storable form and its usable form. When you look at an ObjectID
when it is held in a field of an object, it must be in its storable form. An ObjectID will almost
always be in its usable format when it is in a local variable on the stack, and it must be in the
usable format when it is passed as a parameter to another method.
You can tell at a glance which form any given ObjectID is in by looking at its second nibble, going
from left to right. If bit 2 (going from right to left) in this nibble is set, then the ObjectID is
in its usable form. If this bit is clear, the ObjectID is in its storable form. (For example,
0x84nnnnnnn is a system object in its usable form; 0x80nnnnnn is a system object in its storable
form.) When an ObjectID is about to be stored in an object, it will be converted into its storable
form. Magic Cap will return the usable form of an ObjectID from attributes and operations; when an
operation is entered, all ObjectID parameters will be in usable form. The debugger message you see
means that this conversion was not performed on an ObjectID; you are trying to "use" an
ObjectID that is in its storable form.
The conversion between storable and usable forms of an ObjectID is handled automatically when you
call a getter or setter to get or store a field whose type is an object. This conversion is also
done automatically when you call FieldOf() or SetFieldOf() on an object field. This conversion is
not done when you use ReadFields(), WriteFields(), BeginRead() or BeginModify(). If you use any of
these calls, you must call MakeUsable() when you pull an object out of a field, or MakeStorable()
when you store an object into a field. This conversion is also not done if the type of the field
is Unsigned instead of an object. If you are storing an ObjectID into an unsigned field, you should
either make the field an object type, or call MakeUsable() and MakeStorable() explicitly when you
access that the contents of that field. Additionally, if you keep ObjectIDs in the extra data
portion of your objects, you need to call MakeUsable() on any ObjectID you get out of extra data,
and MakeStorable() on an ObjectID before you store it into extra data. Of course, once you've made
an ObjectID storable, you'll need to call MakeUsable() on it before you can access any of its
fields or pass it along to another operation.
You may ask, "Well, if all that's different between an ObjectID being usable and storable is
setting that one bit, why bother?" The catch is that much more can happen during the
conversion process. For example, if you are getting an ObjectID out of an imported object, Magic
Cap will create a Reference object that refers to the object that you want so that the dispatcher
can switch into the correct context when it calls on operation on this object. Similarly, if you
store an object from your package context into an imported object, Magic Cap creates a Reference
object and stores the ObjectID for the Reference object.
Because any object access (a read or a write) can potentially create a Reference object,
the normal rule about not being able to allocate memory when objects are locked down apply.
This means that you really shouldn't be accessing ObjectIDs within BeginRead()/EndRead() and
BeginModify()/EndModify() sections of code. In the future, there might be tools to catch you if
you make this mistake, but for the present, you need to avoid these situations on your own. If you
find that your code accesses objects within these sections of code but everything seems to work,
you should move the object accesses out of that section; just because the code works on your
machine doesn't mean it will work on your customers'.
Is there any way to statically instantiate instances of a class that lives in another package?
I don't want to have to create these objects at runtime.
Unfortunately, this is the way that the runtime works and there's no way around it. The problem is
that because package class numbers are unique only within the context of that class, there's no
way to specify an instance of a foreign class. The secondary problem is that because of the first
problem, imports must be described as a package context and the native class number, but the
ObjectID of the package context isn't guaranteed to stay the same across clean ups.