// we list the files in a listbox
dim theCode() As BasicCode = Basic1.Codes
for each item as FolderItem in Basic1.files
FIleList.AddFolder(item.name)
// simultaneously add the code from the file to the tag
dim row as integer = FIleList.LastIndex
FIleList.RowTag(row) = theCode(row)
next
Fine, and note that BasicCode is a class instance. The removal of folderItems and tags by
FIleList.DeleteAllRows
or
for i as integer = FileList.ListCount -1 downto 0
FileList.RowTag(i) = nil
next
or
for i as integer = FileList.ListCount -1 downto 0
FileList.RemoveRow i
next
does not get the destructor of the BasicCode class called. This means it’s leaking objects. The BasicCodes and FolderItems both reside in arrays. When the arrays are going out of scope (or being unlocked from the plugin), a subsequent call to depopulate the listbox does not release the objects. If the code that populates the Listbox is removed, the folder items and BasicCodes are released. Am bringing it up here to figure if this is already known, or not.
I very much doubt that your case is related to ListBox.RowTag. As it is not clear from your description what the scope of the arrays are, it is not possible to say more. Do you have a stripped-down version you could upload for us to look at?
I do have a simple project that does not depend on any plugin. It does show the correct behavior. The project that depends on the plugin spitting out arrays has an incorrect behavior. Therefore this issue belongs to the plugin list. But since I am here, I am providing info and it relates to how one should return an array from the plugin to Xojo. Here is the code:
This code is called when the items are assigned to the Listbox. The Listbox sets a lock on each item. The array also had set a lock on each item. The usual way for a single object is to return it to the user after locking it, the general rule. And therefore by not thinking it over, you would lock the array too when giving it to the user, The notion that the Listbox is doing the right thing led to commenting out the locking of the object-array. Thus:
If I remember correctly you need to use REALLockObject for arrays too unless the array has been created with REALCreateArray, then it is already locked.
If a plugin creates a REALobject and provides this REALobject in a getter then you need to lock this object. If you don’t, and the user uses this object in local scope, the REALobject gets unlocked beyond the local scope.
The plugin generates a number of objects and puts it directly in a REALarray created by REALCreateArray. Then at each insertion into the array, the REALobject is unlocked because the insertion into the array gives the REALobject a lock.
You would think that this for a REALarray, not giving it a lock in a getter would suffer the same fate as pointed out in 1 for a REALobject.
The REALarray is only used in local scope, while the objects in this array are assigned to the Listbox , which locks each object. Out of the local scope the REALarray should have been unlocked. My guess is that the locks of the REALobjects to the ListBox prevents the unlocking of the REALarray when it goes out of scope.
The pure sample project, mimicking the plugin, does not give you this hypothetical insight, but it proved that the Listbox was not at fault.
Correct, that’s what is being done, as I said earlier.
The whole reasoning behind it to lock an array in a getter.
Nope, that was the first thing that was ruled out. You lost your bet. The initial post states that if the listbox is not populated, a subsequent call to unlock the array in the plugin is releasing the objects. That already rules out a hidden lock. In addition, calling a double unlock after the listbox is populated does not lead to the objects being destroyed too (since their was only one lock). The only way is to not provide locks on an array in a getter and your and mine point 3 in your post is thus moot when it comes to creating an array from the plugin. It also does not lead to crashes.
Have been doing this 24/7 over the weekend. If you have a plugin that spits out an array, I would suggest to do what I have done. My bet is that you will encounter this too.
static void _initialize_files_and_codes(REALobject instance)
{
ClassData(BasClass, instance, BasData, data);
ScriptPluginLog(cc"in _initialize_files_and_codes because of a reset\
");
if (!data->didResolveEvents)
ResolveBasEvents(instance);
if (data->_resetImportDir)
data->_resetImportDir(instance);
ScriptPluginLog(cc"LockCount files before = %d\
", Locks((REALobject)data->files));
REALUnlockObject((REALobject)data->files);
ScriptPluginLog(cc"LockCount files after = %d\
", Locks((REALobject)data->files));
data->files = REALCreateArray(kTypeObject, -1);
ScriptPluginLog(cc"LockCount new empty array files = %d\
", Locks((REALobject)data->files));
data->fileCount = 0;
ScriptPluginLog(cc"LockCount codes before = %d\
", Locks((REALobject)data->codes));
REALUnlockObject((REALobject)data->codes);
ScriptPluginLog(cc"LockCount codes after = %d\
", Locks((REALobject)data->codes));
data->codes = REALCreateArray(kTypeObject, -1);
ScriptPluginLog(cc"LockCount new empty array codes = %d\
", Locks((REALobject)data->codes));
}
I have a similar method to interrogate the locks on an object. If the getters lock the array then
“before” spits out 1;
“after” spits out 0;
“empty” spits out 1;
The “DeleteAllRows” by the Listbox does not free the objects.
If the getters do not lock the arrays then
“before” spits out 0;
“after” spits out 0;
“empty” spits out 1;
The “DeleteAllRows” by the Listbox frees the objects.
Actually, the problem can be reduced to the following:
It the getter locks the array in the plugin and in Xojo a local variable is assigned to it like:
dim theCode() As BasicCode = Basic1.Codes
and nothing is further done with this array, the array goes out of scope and gets unlocked (value = 0). Yet the items in the array have all a lock count of 1. This then explains that there is no equivalent that can be written in pure Xojo.
Further, an array of FolderItems created parallel to the creation of BasicCode items and is called by
dim theFile() As FolderItem = Basic1.Files
while nothing is being done with, goes out of scope, the array is unlocked (value = 0) and all the items have a lock count of 0 and get released.
After exhausting everything, it appears that you never should issue a REALLockObject in a getter or setter in the context of a REALarray, while a REALUnlockObject on a REALarray in a setter (to remove the existing array and replace it with a new one) is a requirement.
This is because a pluginarray accessed in local scope is not being unlocked beyond this scope. This plugin array can be an array of folderItems too.
This has not been documented as far as I can tell, but noting the above and avoiding locking of an array makes the plugin very stable, no leaks, proper disposal of items of the array. So I guess, it is intentional behavior.