Why McLaren Labs uses Objective-C

McLaren Labs was started with the idea that music and media creation on Linux should be as easy and fluid as Mac OSX. We had been inspired by AVFoundation and the modular way its pieces fit together. We loved being able to build media pipelines with sources and sinks that cleaned up after themselves when you were done with them them.

Many of the facets of the OSX components we liked were provided by ObjC features enabled by the Clang compiler and LLVM tool suite. LLVM has revolutionized language development by paving the way for Swift and Rust. Back at the time we were getting started, Swift on Linux was gaining traction and we considered adopting it. However, after some initial explorations with Swift and libdispatch, we discovered that libdispatch just wasn’t ready with Swift on Linux. That was in 2015 – Swift on Linux is much more mature. The equation might be different today … but it might not too.

  • Shout out to GNUStep and Etoile

Objective-C on Linux is entirely enabled by the runtime of the GNUStep project. GNUStep is a project begun in the 90s to provide an open-source implementation of NextStep (and later Apple) APIs. GNUStep has a dedicated and active development community of very experienced software scientists. The Etoile project and GNUStep worked to develop libobjc with ARC and some other features.

What we like about Objective-C

First and foremost

  • Objective-C is C

This first point means it is easy to interface to devices and toolkits. Using the ALSA (Advanced Linux Sound Architecture) Library for sequencers and sound devices is straightforward. ObjC instance variables are C variables. If you write a method in ObjC that modifies an instance variable in the audio loop, you can be sure that your code is directly modifying only the variable you intend. Interface with pthreads is a breeze, and so is writing callbacks for GTK. It’s all just C, and if you understand the slim abstractions provided by ObjC their implications, it all just works.

  • Blocks

A block is C code block that captures its environment (like a closure) and can be executed as a function. Blocks make it easy to write callbacks that update instance variables in objects, which as we said before are just C variables. The execution of a Block incurs no runtime overhead. When captured, a block’s environment is captured and stored on the heap, incurring a memory allocation during the capture. With blocks we can write code like the example below to open a MIDI device and process events from it.

AlsaSequencer *seq = ... (omitted) 

[seq onMidi:(AlsaEvt *evt) {
… (use MIDI event)
}];
  • Automatic Reference Counting

Automatic Reference Counting (ARC) of object references is enabled by LLVM and essentially allows us to forget about freeing objects. Upon creation, an object is given a reference count of 1, and as references to it are passed around, Clang injects the right calls (all inline!) to keep track of how many objects are referencing the object. When the number drops to 0, the object is deallocated.

Objective-C makes it easy to extend and object’s deallocator so that Audio and MIDI devices can be cleaned up and GTK objects can be released. This helps make use of devices as fluid as the use of data objects or GUI elements.

ARC performs the chore of keeping track of your objects when their temporal lifetime is not aligned with their lexical lifetime. This capability makes it possible to pass references around in pipelines, and easily deallocate them they are no longer needed.

  • Grand Central Dispatch

Apple released Grand Central Dispatch (GCD) as an open source C library known as libdispatch. GCD brings together many event sources on Linux (timers, file descriptor events, event signals) and also provides high-performance queues on which to handle events. Combined with blocks and ARC, this provides a really nice environment for handling events.

For instance, in our system, we have a low-latency MIDI queue, and also the main queue dedicated to the GUI. Receiving a MIDI event on the low-latency queue and scheduling a GTK drawing event on the main queue looks something like the following.

[seq onMidi:(AlsaEvt *evt) {
uint8_t note = evt.data.note;
dispatch_async(dispatch_get_main_queue(), ^{
gtk_foo_update(note);
});
}];

Why does this matter? In our example, we have scheduled an event handler in one thread from another in a concise, easy to reason about way. The block captured the note variable, which relieves us from creating a boiler-plate struct for a storing an integer.

  • Foundation

Foundation is the standard library of Objective C, and is GNUStep’s gift to the software world. It includes thread-safe numbers, strings, arrays and dictionaries. It includes byte arrays, and a nice thread-safe logging facility. Foundation also includes KeyValueObserving, which helps at the GUI level where changes to objects trigger events. After three decades of development, this all works together pretty nicely.

Conclusion

If you thought Objective-C was just for Apple, or that now that Swift has arrived it is entirely dead, you might want to look again. With very modern compiler support from Clang/LLVM and an experienced community advancing the runtime, Objective-C has a lot to offer. Check it out at some of the references below.

References

http://www.gnustep.org/

http://wiki.gnustep.org/index.php/ObjC2_FAQ

https://github.com/gnustep/libobjc2

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.