I’m a bit confused on when to lock and unlock objects and strings. Say for example:
void func(REALobject instance, REALstring blahString)
Say I am using blahString
only in this function to pass onto C. Do I lock blahString
at any point? Should I unlock it before returning?
void func(REALobject instance, REALstring blahObject)
Say blahObject
is a FolderItem
and I am going to call NativePath
on it using it only in this method. Should I lock it? Should I unlock it before returning?
Thanks for any pointers on this subject, even pointers to where to go read about it (in detail). Looking at the Plugin SDK docs, I come away confused as to when in these cases.
Xojo uses a simple lock/unlock model:
- Values passed into your function should be considered to have a +0 lock count.
- Values you get handed back from invoking framework or invoking user functions will have a +1 lock count.
- Values you get handed back from REALGetPropValue will have a +1 lock count.
- Values you return from your function should have a +1 lock count – unless you raise an exception.
- Calling unlock decrements the lock count by 1, potentially deallocating the object.
- There is no garbage collector, so avoid reference cycles.
So, let’s put a bunch of these rules into practice in a bit of a contrived example:
// Takes a FolderItem and returns its path with a trailing slash if it's a directory.
static REALstring PathWithTrailingSlash(REALobject instance)
{
Boolean isDirectory;
assert( REALGetPropValue( instance, "Directory", &isDirectory ));
REALstring nativePath;
assert( REALGetPropValue( instance, "NativePath", &nativePath ) );
if (!isDirectory) {
return nativePath;
}
REALstring result = NULL;
REALstringData pathData;
if (REALGetStringData( nativePath, kREALTextEncodingUTF8, &pathData )) {
if (pathData.length) {
const char *utf8Data = static_cast<const char *>(pathData.data);
if (utf8Data[pathData.length - 1] != '/') {
REALstring temp = REALBuildString("/", 1, kREALTextEncodingUTF8);
result = REALAddStrings( nativePath, temp );
REALUnlockString( temp );
} else {
// We're unlocking nativePath before we return, so we need to bump
// the lock count on this here.
result = nativePath;
REALLockString( result );
}
}
REALDisposeStringData( &pathData );
}
// If anything failed and result is still NULL when we return, that's okay.
// NULL is the canonical way of representing an empty string.
REALUnlockString( nativePath );
return result;
}
Note that this elides some error checking for simplicity. For example, you should check ‘instance’ for NULL and raise a NilObjectException.