In MoSync, you can use handles to refer to data objects, such as binary data, images, audio data, text data and other resources. In this guide we take a look at placeholders — a type of handle that is used to refer to dynamically created resource im memory, and at the syscall functions that manage them.
There are two ways of creating data objects. The first way is to define them as resources in a MoSync resource file. (This is what is typically meant when we talk about "resources" — data objects that are bundled with your application.)
The second way to create a data object is to create it dynamically in run-time. Dynamically generated data objects and data objects defined in resource files can be used in much the same way by your application. But there are also important differences between them, perhaps the most important being that you can use the MoSync sycall function maDestroyPlaceholder to remove placeholders (and associated data) on dynamically allocated data only.
A MoSync program has two spaces of memory. The first is C/C++ memory and is the addressable memory that contains the C/C++ heap, stack, and static data. When you use malloc or new, you allocate data in this memory space. The C/C++ memory space has a fixed size, you set it up in the Eclipse IDE build settings (or you can alternatively specify this on the command line).
The second memory space lives outside of the C/C++ memory, in the native application space. This is where resources/data objects are stored, and we call this space resource memory. This memory space cannot be accessed with C/C++ pointers. Instead, you use handles to refer to data in this space. Furthermore, the size of the resource memory is not fixed; the amount of memory actually is available depends on the platform and the hardware of the device on which the application is running.
Handles are just integers. They are used to refer to data objects in resource memory. (MoSync maintains a lookup table with handle to data references.)
A placeholder is a handle that does not yet refer to any data object. In MoSync, when you wish to use dynamically allocated data objects, you first create a placeholder. That creates an entry in the handle table for you. Then you can use the placeholder in syscalls that create data objects. The placeholder then becomes an actual handle to the data.
There are several ways to dynamically create data objects. You can read data from a file, or over the network, or you can create an image or a sound. Examples follow below.
When you wish to free up a dynamically allocated data object, you can use the syscalls maDestroyObject and/or maDestroyPlaceholder.
The rule of thumb is to use maDestroyPlaceholder. This is a syscall introduced in MoSync 3.0. Like maDestroyObject it deallocates the data object, but it also frees the placeholder.
Calling maDestroyPlaceholder prevents the placeholder array in the MoSync runtime from growing when you have many calls to maCreatePlaceholder.
(Previously there was no way to free a placeholder in the runtime, but you could have used MAUtil::PlaceholderPool to reuse placeholders. Not that, from MoSync 3.0 MAUtil::PlaceholderPool is deprecated.)
For maximum efficiency, you should replace calls to maDestroyObject in your existing code with calls to maDestroyPlaceholder, given that the placeholder is not reused. But never use maDestroyPlaceholder if the placeholder is going to be reused!
In the first example we read a PNG image file from a data store and display it, them we deallocate the image data:
// Read and image and display it, then // deallocate the image data. MAHandle image = maCreatePlaceholder(); MAHandle store = maOpenStore("cat.png", 0); maReadStore(store, image); maDrawImage(image, 0, 0) maUpdateScreen(); maDestroyPlaceholder(image);
This is a useful technique if you have lots of images in your application, and all of them will not fit in application memory.
Next example shows how to write a block of memory allocated on the C heap to a data object, and then later read it back.
// Allocate a block of data. void* pData = (void*) malloc(DATASIZE); // TODO: Write data into the data block. // Create a handle and write the data. MAHandle data = maCreatePlaceholder(); maCreateData(data, DATASIZE); maWriteData(data, pData, 0, DATASIZE); // Now the data exists in "resource memory" and we // can free the C data block. free(pData);
When you need the data, read it back again:
// Allocate data block. pData = (void*) malloc(DATASIZE); // Read data from the handle. maReadData(data, pData, 0, DATASIZE); // TODO: Now the data is in C memory and you // can access it using the pointer.
Finally, when you are done with everything, make sure both the data block and the data handle are deallocated: