Xojo Tips and Tricks

From MBS Xojo Blog:

Xojo Tips and Tricks

We got a few tips and tricks collected for you in the last week.

1. App.Constructor

If you need code to run really first, you may go with the constructor of the Application object. This constructor runs before the Opening event, so it is your chance to do very early initialization before all other events run. But using Constructor can be tricky since you need to call the constructor on the super class and match the signature, which is not always easy to know. Let us show you how the constructors would like

Console and Desktop targets:

Sub Constructor()
  System.DebugLog CurrentMethodName
End Sub

Web target:

Sub Constructor()
  System.DebugLog CurrentMethodName

  // Calling the overridden superclass constructor.
  Super.Constructor
End Sub

iOS target:

Sub Constructor(launchOptions As ptr)
  System.DebugLog CurrentMethodName

  // Calling the overridden superclass constructor.
  Super.Constructor launchOptions
End Sub

For some projects we use constructor to put in some logging or plugin based exception catching very early to catch issues before App.Open event runs. See NSExceptionHandlerMBS and GlobalExceptionHandlerMBS classes.

2. No sessions early on

If you call functions like SessionAt() or SessionWithIdentifier() too early, you may get a nil object exception as the session array is not yet initialized. Xojo keeps their sessions in some kind of storage and this gets initialized somewhere in the app initialization, but it is possible to have code run early enough, that the storage is not initialized yet.

3. Caps Lock state

You can figure out if user has caps lock pressed with Keyboard.AsyncKeyDown function. e.g. you may run a timer once a second to check it. The key to check on macOS is number 57.
And if it is pressed, you may show a warning in a dialog with a password field.

For Windows we recently added a new WinKeyIsCapsLock function for RemoteControlMBS module:

// check state in a timer
If RemoteControlMBS.WinKeyIsCapsLock Then
   Title = "caps lock on"
Else
   Title = "caps lock off"
end if

By default macOS shows caps lock state in password fields nowadays.

4. RowTag() checking performance

What is faster with using int64 for RowTag in a Listbox and checking them?

If RowTag(row) = SomeInt64 Then x
or
If RowTag(row) = VariantInt64 Then x

So either you compare RowTag to the value or to a variant including the value. The first one gets the variant from RowTag and then converts the value to an Int64. The int64 is then compared to the other value. In the second case the RowTag calls the VariantCompare function, which is a bit slower to do the Int64 compare as it is an extra step around it. So the first version is a bit faster.

5. Check string for empty performance

Which one is faster:

If SomeString = "" Then x
If SomeString.Length = 0 Then x
If SomeString.Bytes = 0 Then x
If SomeString.IsEmpty Then x

Technically Xojo’s compiler should optimize all three to a nil check for the string variable, since in Xojo an empty string is just passed as nil value around internally.

Invoking .len or .length both starts counting characters for UTF-8 strings, which can take a while, so this is the slowest version. The call to .Bytes is taking a bit call since it invokes a few functions. Faster than Length as it just reads the byte count from the string structure. Now to our surprise the comparison to “” is the fastest, even as it invokes a function to compare strings. But this function is optimized with empty strings in mind and exists quickly, if one is nil and the other not.
Update: We added IsEmpty to our checks, which internally calls Length, so it’s even slower than calling Length directly.

So if you need to check if a string is empty, please compare to “” to check for empty strings.
If Xojo improves in future to compare to nil without a function call (see #64074), you will benefit from all the changes.

9 Likes

Is this the correct way in Xojo?

Sub Constructor()
  MyThings()        // base object not initialized yet, unexpected things could occur
  Super.Constructor // init base object, you could mess with previous "things"
End Sub

Because in some environments the expected order would be the inverse

Sub Constructor()
  Super.Constructor // prepare the base object
  MyThings()        // do your extensions on top of it
End Sub

Well, since super.Constructor may fire the Open event and our goal is to be before there app.Open event, we may want to put our code before the call of super.Constructor intentionally.

I would expect ways of overriding the Open event in the base class so it could not fire there, and fire it explicitly from the subclass after our extended initialization.

1 Like

The only reason to subclass the constructor would be to do some initializtion before calling super.constructor. Otherwise, you might as well just use the Open event.

You subclass a class, a subclass can subclass another subclass building a chained inheritance. During the init phase of the instantiation of a class the constructors of that hierarchy should follow a sequence up to down providing proper initialization of the details of each part of the hierarchy, like setting some values read from arrays, dictionaries, databases… and the init chain keeps going down the hierarchy where the next level probably depends of data set from the previous level. Like Classes Animal → Mammal → Dog
Dog is a subclass of Mammal, Mammal is a subclass of Animal. When you create a Dog, you first construct an Animal, then you construct a Mammal using a proper ready Animal class, then you construct a Dog using a proper ready Mammal ready class, so to build a dog you need:

Animal {
  Animal.Constructor {
    init animal things
  }
}

Mammal extends Animal {
  Mammal.Constructor {
    super.constructor // prepares the animal structures to build my mammal
    do my mammal settings
  }
}

Dog extends Mammal {
  Dog.Constructor {
    super.constructor // Prepares the mammal structures to build my dog
    do my dog settings and now I have a dog
  }
}
2 Likes

@Christian_Schmitz, could you repeat this test case with Xojo 2023 Release 4 Build 61578?

 If SomeString = "" Then x
 If SomeString.Length = 0 Then x
 If SomeString.Bytes = 0 Then x
 If SomeString.IsEmpty Then x

What is the rank of IsEmpty now?

For 23.4.0.61578 relative to each other:

 If SomeString = "" Then x -> 17
 If SomeString.Length = 0 Then x -> 27
 If SomeString.Bytes = 0 Then x -> 20
 If SomeString.IsEmpty Then x -> 21

Tested on macOS with Apple Silicon. With “hello” as string. If the string is longer, Length gets worth.
So basically = “” has a shortcut, IsEmpty should now be a thin wrapper around Bytes call.

2 Likes

A smart compiler should learn that equivalence and always emit the same machine code used in SomeString = "" (the faster one) for all 4 cases.

1 Like