Memory Runtime


What happens when a new object is created at runtime, as NewTransient() or NewNear() does? Is there a constructor-like function as in C++? How do you initialize the object? Do you have to explicitly call an Init() method after New()?


Notice that the NewXXX calls have a parameter on the end called "parameters". Init will be called for you, and will be passed the "parameters" parameter. All of the fields of the new instance will be set to nil before Init is called, so if nil is a fine default value then you don't need to worry about anything. Each class has a different idea of what kind of parameters structure is appropriate, and all the ones for the system are defined in {MagicDeveloper}Interfaces:NewParameters.h.

You must always call InheritedInit in any Init methods that you create for your classes. Here's what Viewable_Init looks like. You can see that it sets some of its fields to defaults it likes better than nil, and sets some of its fields according to the structure that gets passed in (if any):

Method void
Viewable_Init(ObjectID self, NewViewableParameters *paramsPtr)
{
   InheritedInit(self, nil);
   SetField(self, viewFlags, canMoveMask | canCopyMask | canDeleteMask |
                             visibleMask);
   SetField(self, color, rgbBlack);
   SetField(self, altColor, rgbWhite);
   SetField(self, labelStyle, iBook12);
   if (paramsPtr != nil) {
      SetFieldOf(self, Viewable_relativeOrigin_h, paramsPtr->relativeOrigin.h);
      SetFieldOf(self, Viewable_relativeOrigin_v, paramsPtr->relativeOrigin.v);
   if (paramsPtr->superview != nilObject)
      SetSuperview(self, paramsPtr->superview);
   }
}

If you're creating a new viewable, you might do it like this:

   NewViewableParameters viewableParameters;
   ObjectID newVieweable, superview = iScreen;
   Dot relativeOrigin;

   relativeOrigin.v = 0; relativeOrigin.h = 0;

   SetUpParameters(&viewableParameters.header, newViewableObjects);
   viewableParameters.superview = superview;
   viewableParameters.relativeOrigin = relativeOrigin;

   newViewable = NewNear(Viewable_, self, viewableParameters);

Creating a new viewable this way is seldom done, this is just an example of passing parameters for initial values upon creation. Most of the time you need a viewable you won't create a brand new one that needs to be initialized. You'll either use a viewable that already exists (defined at compile time in Objects.Def) or is a copy of an existing viewable (copied by the user with the copy tool or totebag, or made from a prototype by using CopyNear() on an instance in the system or in a package).


How do you decide whether to use NewTransient() or to use NewNear ()? When is it important to create objects in a specific cluster?


Objects should be allocated in transient clusters only if your program can tolerate them going away when the user shuts off the power to the communicator. Objects which are created and destroyed in the same function are good candidates for transient memory. Objects that are storing data for the user should almost never be in transient memory.

NewNear() is used when you want an object to be allocated in the same cluster as another object. In general, only a class's operations know where objects referenced by instances of that class should be located. When setting fields of objects that refer to other objects, you should use the appropriate setter method if it's available to allow the class to move your object into the same cluster as the container object if it so desires.


When my package persistent cluster runs out of space, does Magic Cap try to resize the cluster to make more room? When does Magic Cap make use of the storage available on an inserted RAM card?


Clusters can grow dynamically, but if you want to be able to allocate items on a RAM card when it is present, the best way for you to do this is to use the Preferred memory routines: NewPreferred(), CopyPreferred(), MovePreferred(). If the user has decided that they want items to be allocated on the card by checking the "New Items Go Here" button for the card in the Storeroom, then the Preferred calls will create new objects there. The suffix "Preferred" refers to the preferences of the User, not necessarily General Magic, Magic Cap, or your package.


How can I found out how much memory is available on the container that my package objects resides in? For example, if my package persistent cluster lives in main memory, I want to report the number of free bytes in K in main memory, but if the user has New Items Go Here set to a RAM card, I want to report the amount of space available on the RAM card.


The right combination of magic words to do this are:

freeKBytes = TotalFreeKBytes(ObjectContainer(somePackagePersistentObject));

This will give you the number of K free in main memory or RAM card, whichever your package resides on. This will give you a number very similar to what is displayed in the Storeroom, which is probably what the user is expecting anyway.


I'm running my package from a RAM card, but I've noticed that the uncommitted changes for my package accumulate in a cluster in main memory. What should I do to get the objects to reside on the PC card?


Uncommitted changes shadow clusters are located in transient memory. If they were stored in persistent memory, then there would be no need for an uncommitted changes cluster in the first place. The System commits changes at certain times, but if you are filling up transient memory with uncommitted changes you may need to commit your changes more often.

You can call:

UpdateAll(SystemContext_);

This will commit all changes to persistent memory, and free the transient memory associated with the uncommitted changes. However, it may take awhile so don't call it gratuitously.


What's the difference between putting my package on a RAM card versus a ROM card?


The only difference (at least, the only one that matters) between RAM and ROM cards is how the system handles changes to your package objects. For a package on a RAM card, the changes cluster is located on the RAM card. If you drop a stamp in your scene, and you take the card to another device, the stamp will still appear in the scene. For packages on a ROM card, the changes cluster is located in main memory. If you remove the card, the changes cluster is still present in main memory, and will be reattached to your package when the card is reinserted. If you take the card to another device, any changes you made to you package on the original device will not show up on the new device.


Is Magic Cap's garbage collection able to regain the space occupied by orphaned objects? If a user places a viewable object from my package into a confirmation window (for example), will the garbage collection find that viewable and destroy it?


Magic Cap is normally able to regain the space occupied by orphaned objects. The catch is that Magic Cap defines "orphaned" as an object that is not referred to by any other object. In this particular instance, the set up for confirmation window refers to the dropped viewable through its subview field, so your viewable is not really orphaned, so in all likelihood, it will remain in memory.

There are instances where the memory will eventually get reclaimed, but that requires that your package be removed from the device (or at least packed up). When this happens, the viewable in the window would get replaced by a stub object, as the system no longer knows about your package classes. When your package is reintroduced, the viewable will not get reinstalled into the window, and would then be able to get garbage collected.

Finally, one last way the memory occupied by the viewable can get reclaimed is if the device gets so low on memory that the device presents the user with a list of objects that can be thrown away. There is a good chance that the viewable will get tossed in this scenario.


I'm testing my package with New Items Go Here set to go to a RAM card. I noticed that tons of Reference and LinkableReference objects are being created in the system persistent cluster. I don't create any of these objects. Where are they coming from?


Because ObjectIDs are only unique within a package, the system cannot infer the package an object came from if it has just the raw object ID. Basically, whenever you refer to a package object from a context other than that object's native context, a Reference object is created. The reference object lets the system know what the object's native context is, and what the object's ID is in that context. When you check NIGH on a RAM card, you are essentially creating a data package to store objects in. All objects in this package are referred to by references. These references will be in system persistent memory. This is normal overhead for using NIGH, and there's nothing you can do about it.


I've noticed that most system persistent objects go into slot 11, but it seems like they live for brief periods (just after being created) in slot 8. What is Magic Cap doing?


A persistent object can live in one of three clusters: the source cluster (the objects from your Objects.Def file), the committed changes cluster (also known as the shadow cluster, the persistent committed changes cluster, or the changes cluster), and the uncommitted changes cluster (persistent cluster).

Despite its name, the persistent cluster (system or package) actually lives in transient RAM. When you create a new object with a call to NewPersistent, NewNear near a persistent object, or NewPreferred, or if a change is made to an object in the source cluster, it will first get created in cluster 8. Periodically, the system will perform an update, where changes get committed. During this process, objects will get moved from the uncommitted changes cluster in transient memory to the committed changes cluster in persistent memory. For system objects, this will seem like objects move from cluster 8 to cluster 11. For package objects, they will move from cluster B to cluster 13.

When you want to access a particular object in system persistent memory (an object whose ObjectID starts with an 8), the system will first search in cluster 8 for that object, then in cluster 11, and ultimately in the system source cluster, cluster 10. Likewise, if you want to access a persistent package object (an object whose ObjectID starts with a B), the system will first search cluster B, then cluster 13, then cluster 12 for that object.

So, if an object lives in cluster 8, but not in cluster 11 or 10, if the machine resets, it might seem to the user that he has lost some data. As a matter of fact, if you tap the revert button in the magic lamp, it's basically throwing away the uncommitted changes clusters, and reverting back to the versions of the objects from the last time a commit operation was performed.


I've noticed that after a crash, system indexicals will contain nilObject instead of the package object that I had installed. Why does this happen? How can I help the system clean up in these cases, and restore the appropriate values for these indexicals?


The system will perform object cleaning after a reset, or after the PC card slot state has changed. During cleaning, direct references inside objects within one package context to objects in another package context (or the system context) are set to nilObject first. Then, references to transient objects are cleaned. Third, references in objects in the system context to references to objects in packages are cleared. This is done to make sure there are no references to objects that no longer exist after a reset.

If you changed these system indexicals by using System_InstallInto instead of SetIndexical(), the system will automatically restore them to their original values during the cleaning process. For example, if you wanted to replace the fax progress bar, you would use the following call:

InstallInto(iSystem, progress, 0, iFaxProgress, 0);

System_InstallInto has code which remembers the old indexical value, so when System_MakeValid is called, it restores the old value if the indexical contains nilObject. By doing this, you can be sure that iFaxProgress will get healed regardless of whether or not your package is around.

You can continue to use SetIndexical() to restore the old indexical value in your restore code.

One thing to be aware of is that this mechanism doesn't nest changes. If another package has previously changed an indexical that you want to change, System_MakeValid will restore the indexical to the original value instead of the penultimate one.


If my package lives on a ROM card and it calls NewNear(..., self, nil), will an exception get thrown or will the data be allocated in system persistent memory correctly?


When your package calls an xNear() operation, that basically means that you want the new object to reside in the same cluster as object you pass as the second parameter. For packages in ROM, the package persistent cluster lives in main memory, so any object created near a source object or a persistent object will be created in this cluster in main memory.

You need to be careful about this situation because you'll be taking up memory space that would normally be available to the user to store other types of data. If you can use NewPreferred() instead, you probably should.


If I use NewPreferred() to create my objects, will they get scattered all over the place if the user frequently changes where new items should go, or does Magic Cap move all the objects into the new place where new items should go?


Magic Cap doesn't move any data that's already in the device. The user can switch NIGH with impunity; it just means that your package will have data in its persistent cluster, as well as references to objects in the new items package.

The proper way of respecting where new items should go is by using NewPreferred() to create the root objects that can be filed by the user from the filing window. Essentially, this basically means Card subclasses at the moment. Other objects should be created with NewNear(). For example, if you create a new card instance, you should create the Card object with NewPreferred() or CopyPreferred(). As you fill in fields of this object with other objects, you should use NewNear() to create these sub-objects in the same cluster as the root card object.


How can I get my package to create objects in the new items data package instead of in the package's own cluster?


Users can choose where data should be stored by tapping on the placards in the storeroom and checking the new items box. If the user chooses to put new items on a PC card, a "new items" data package is created on the PC card. You can use the preferred memory calls, NewPreferred() and CopyPreferred() to create objects in the memory container that the user wants his data to be stored. If main memory is where new items should go, the preferred memory calls will create objects in your software package, even if your package is located on a PC card. If the user wants new items to be stored on a PC card, using the preferred memory calls will create objects in the new items data package.

The concept of preferred memory really only applies to viewable objects that can be filed by using the file command in the magic lamp. The vast majority of these types of objects are different types of cards or datebook tasks, however, any package could create new types of fileable viewables.

You don't want to use NewPreferred() or CopyPreferred() to create non user-visible objects like ObjectLists or Text. This is because if these objects wound up in the new items package, the user would have no way of moving them out of the data package because there is no viewable object that they are associated with. What you want to do is use these calls to create the high level user-visible object that can be filed, like a card or task, then create lower level objects near this object using the near memory calls, NewNear() or CopyNear().

For example, imagine you want to create a telecard that had a bunch of stamps, some text, and a minicard that represents an enclosure. To respect the user's setting of where data should go, you would create the telecard with a call to NewPreferred(). This would create an object that resides either in your software package, or in the new items package. You would then create the subview objects using one of the near calls. This is how you would get non user-visible objects into the correct memory container. Because these objects are associated with a higher level viewable, you won't have objects that the user wouldn't be able to move out of a data package. The user would also be able to move the viewable to different memory containers and keep the associations between all the objects intact.