Swift Optional Chaining Performance

Optional chaining in Swift offers a convenient mechanism for testing an optional value embedded in a statement without having to bother with messy binding or dangerous implicit unwrapping. It’s basically just a bit of syntactical sugar that internally converts something like this:

foo?.bar = 42

into this:

if let unwrappedFoo = foo {
  unwrappedFoo = 42

This is fine when used in moderation. However I occasionally run across code like this:

foo?.bar = 42
foo?.baz = 3.14

This makes me a little itchy. My worry has been that the compiler then generates code as if it encountered the following:

if let unwrappedFoo = foo {
  unwrappedFoo.bar = 42
if let unwrappedFoo = foo {
  unwrappedFoo.baz = 3.14
if let unwrappedFoo = foo {

You would (hopefully) never write something like this but that’s how the compiler is going to interpret all those chained optionals… Or is it? Maybe the compiler is smart enough to figure out what is going on here and I should just relax and let it do its thing?

Nah, I need to know what’s going on. So I cobbled together a few contrived examples in Xcode and ask it to generate some assembly for me… Except Xcode can’t yet show you the assembly for a Swift file. Sigh. Okay well Google can probably tell me how to look at the assembly and sure enough I find this lovely article (which, by the way, also introduced me to Hopper which is pretty awesome).

Armed with Hopper and a bit of knowledge I set about examining the assembly produced with a variety of techniques and optimization levels with my sample code.

My first test was an unoptimized test of a function using optional binding versus the equivalent using optional chaining (letTest vs chainTest in the sample code) and which yielded assembly with the following lengths*.

Unoptimized Opcode Count
Optional Binding 138
Optional Chaining 248

As I suspected, the optional chaining was much less efficient. Not really surprising, until I examined the same functions with optimizations turned on.

Optimized Opcode Count
Optional Binding 87
Optional Chaining 82

Wait, what? The compiler was somehow smart enough to figure out what I was doing and doesn’t just match the optional binding approach, it beats it. Looking over the assembly, it appears the optional binding approach included an extra retain / release.

After the first batch of results the relaxed approach is starting to look better. Maybe I just hammer on the keyboard and the compiler somehow just figures everything out for me. But first another test. This sample is identical except these are methods instead of global functions. First the unoptimized results.

Unoptimized Opcode Count
Optional Binding 147
Optional Chaining 195

Actually a bit more respectable here than the global counterparts, but optional binding is still much more efficient. And the optimized results…

Optimized Opcode Count
Optional Binding 102
Optional Chaining 132

Interesting. This is what I expected originally. But why the difference between a method and the function? I imagine because the variables could have setter functions or observers which could alter the value of tObj, therefore the compiler can’t be confident that it does not have to test the value of tObj for each assignment.

In the end, using a series of optionally chained statements is not horrible and in at least one case actually faster than optional binding, but personally I’m going to continue to do what I can to provide those additional clues to compiler and future maintainers of my code (including myself) as to my intent where practical.

Of course this just goes for a series of optionally chained statements. If I’m only evaluating that optional once (maybe even twice if I’m feeling naughty) then optional chains are perfect. Any more than that though and it’s getting wrapped in an optional binding.

*Using the length of the generated assembly as a measurement of efficiency is not always the best idea. The compiler could be unrolling loops or any number of optimization techniques that don’t end up generating less code. However this example is pretty simple and serves as a decent yardstick here.

A Few Toys

Since the release of Terrella and the big update to Super Speller I’ve slowed down a bit with Quiet Spark work. But that doesn’t mean I’ve completely stopped working on personal projects. There are a few things over on github that I’ve been picking at here and there as the mood strikes me.

First I have a Swift based video poker app for iOS. It’s called Poker. Clever eh? I’m not sure I would use any of that code in anything real since it was mostly a testbed that I used to teach myself Swift. Very basic. Except maybe the expected value calculations that I added. There are probably more efficient ways to calculation EV. I just used a brute force method, but I was trying out different ways of iterating over objects and evaluating ~1.5 million poker hands was a good way to put Swift through its paces.

Next up, a very simple app to try and find bundle and sandbox locations of apps installed on the simulator in Xcode 6. What with the fifty-eleven different simulator configurations I understand why Apple decided to rework the simulator locations. I’m not sure why there isn’t an easy way in the simulator or Xcode to get to these locations though. So I wrote SimDirs (another clever name) to try and track these locations down. I’ve not been able to find a 100% reliable way to do this though. Feel free to improve upon it.

Finally, I’ve been trying to do a better job giving back to the developer community, hence the recent activity not only on github, but also StackOverflow. I try to keep this stackoverflow page open and skim through the questions as time allows. But what I really wanted was an app that would let me see new questions and hide those I’d looked at and/or ignored. So I wrote… wait for it… StackMonitor. Again, nothing fancy. I’ve not even put this one through its paces for a whole day yet actually, so who knows how well it’ll work in practice.

The SimDirs project has actually seen a bit of interest so I was motivated to try and make it a bit better. There’s definitely still room for improvement so I may continue to pick at it and the others as the mood / need strikes me.

Tracking Down Storyboard Warnings

Xcode seems to have this annoying problem where sometimes, most of the time for me, when I select a storyboard warning it will open the file, but won’t show me the item that’s affected. This is becoming a real problem now that layout margins and Automatic Preferred Max Layout Width on UILabels seem to be the default.

Reveal compiler output
Reveal compiler output with this thingy

I’m working on a project that contains a pretty huge storyboard where trying to find the culprit would be like looking for a needle in a haystack. Fortunately I discovered a trick. Not sure trick is the right word but whatever. Here’s what you do. Switch to the Report Navigator and select your most recent built. Make sure “All Issues” is selected and locate the storyboard that contains warnings. Then click the hamburger looking button to the right to see the compiler’s actual output.

Storyboard IDs
Compiler output shows the storyboard IDs

After expanding the compiler output, you should see a list of your warnings towards the end. These warnings include the storyboard path along with an identifier and the warning text. Copy the identifier for the warning that you want to investigate and then paste that into Find In Workspace (⌘⇧F).

Find In Workspace with storyboard elements
Find In Workspace with storyboard elements

Voila! Xcode should display the problem storyboard element as a result. When you click on the result it will open the storyboard and select the element in question allowing you to fix the edit.

Maybe this is a thing everyone already knows about but it was news to me when I accidentally stumbled across it a little while ago. Hopefully you find it useful.

Backwards Compatibility on the App Store

The imminent release of iOS 8 and the new iPhones from Apple got us thinking about updating our Quiet Spark apps to take advantage of some of the new features that will be available. Most importantly (at least to us) are the new adaptable UI APIs. First on that list, since it’s still fresh in my mind, is Terrella.

Terrella currently requires iOS 6.1 or greater. It supports 6.1 for a few reasons. First, 6.1 was the most recent release when I started on it. Although it would have been pretty easy to switch to iOS 7 when it arrived, my kids did not love iOS 7 and wanted to continue using iOS 6. I did too for that matter. I also wasn’t certain what the adoption rate would look like and I really didn’t need anything that 7 added. So I continued to design with support for iOS 6.1.

Back to the present. While I’m sure there is a way to add support for adaptive UI to Terrella and maintain compatibility with iOS 6.1 it would probably be messy and a pain. Also we were thinking perhaps we would be more likely to be featured if our game included support for all of the latest goodies. That’s what I’d always heard anyway, so we decided to take a look and see what everyone else was doing and the results were surprising.

First we looked at the Games category. Specifically we looked at the “Best New Games” and then the top 20 apps in the various Top charts. The Top charts may not represent the latest and greatest, but are still useful to see if users are specifically looking for apps that include support for features only available in the latest releases of iOS.

Side note: for whatever reason there were only 18 apps featured in Best New Games yesterday when we gathered this info.

iOS Version Best New Games %* Top Paid % Top Free % Top Grossing %
7.0 1 5.6% 0 0.0% 3 15.0% 1 5.0%
6.1 0 0.0% 1 5.0% 1 5.0% 1 5.0%
6.0 11 61.1% 2 10.0% 8 40.0% 2 10.0%
5.1 1 5.6% 2 10.0% 0 0.0% 4 20.0%
5.0 3 16.7% 6 30.0% 5 25.0% 8 40.0%
4.3 2 11.1% 9 45.0% 3 15.0% 4 20.0%

Not only do the vast majority of the featured Games only require 6.0, more than a third support 5.1 and earlier. Two of these new releases go all the way back to 4.3! This was a bit of a surprise. Only one of the apps in that list requires 7.0. While this is just one sample, it does call into question the notion that Apple is pushing more apps that run on the most recent release of iOS.

In the top charts support for older version of iOS is even more pronounced. Nearly half of the Top Paid games support iOS back to 4.3. You have to wonder if this is by design. There are some heavy hitters in the games category with the resources to determine if including support for these older versions make sense. While Apple claims that the iOS adoption rate is well over 90% I wonder exactly how that is measured. Perhaps there are more users running older versions of iOS, especially in the Games category. Consider that the Games demographic is likely skewed much younger than, say, the Productivity category. And, if other families are anything like my own, older devices tend to get handed down from child to child when the parents upgrade their devices. Could that be what’s going on here?

Next we decided to do the same breakdown by looking at the Overall category. The results here were much more in line with what I expected.

iOS Version Best New Apps %* Top Paid % Top Free % Top Grossing %
7.0 16 80.0% 1 (0) 5.0% 7 (1) 35.0% 3 (1) 15.0%
6.1 0 0.0% 1 (1) 5.0% 1 (1) 5.0% 0 (0) 0.0%
6.0 3 15.0% 6 (1) 30.0% 10 (3) 50.0% 4 (2) 20.0%
5.1 0 0.0% 1 (1) 5.0% 0 (0) 0.0% 2 (1) 10.0%
5.0 1 5.0% 6 (5) 30.0% 1 (0) 5.0% 7 (7) 35.0%
4.3 0 0.0% 5 (5) 25.0% 1 (1) 5.0% 4 (2) 20.0%

80% of the apps featured here require support for iOS 7 or greater and only 1 includes support for those still stuck on iOS 5.0. Here it very much seems that Apple is pushing apps that would also encourage users to adopt the most recent version of iOS. That or developers in other categories are not quite so concerned with the hand-me-down theory I mentioned earlier.

The overall Top charts aren’t as useful here as most of the apps here are games. The number in parenthesis is the number of apps that were games. Although it is interesting that in the Top Grossing category there are 2 apps supporting iOS 4.3 that are not games.

At the end of the day, this doesn’t make my decision to drop support for iOS 6.1 in Terrella any easier. In fact it’s even less clear to me now. The decision on which versions of iOS to support is much more nuanced that I originally thought. Especially if you’re creating a game.

Catching Up

Now that Terrella is done I can turn my attention to a bunch of other stuff I’ve been neglecting for far too long, not least of which are my other apps (working on it). But there are some things around here that I’ve updated recently as well.

First, the genealogy section of SGnTN has been updated from TNG v7 to TNG v10. Yeah I was a little behind with that. But hey it’s all updated and shiny now so if you’re one of my genealogy visitors please take a look. And if you’re looking to create you’re own genealogy site, definitely take a look at TNG. The software and the support are top notch.

Besides those interested in genealogy, I get a lot of visitors looking for information on iOS release and device compatibility. Specifically, this post I made 2 years ago. I’ve finally taken some time to update that information and give it a permanent home. Hopefully I’ll be updating it next week when the new iPhones are announced.

Now if I can just find a minute to update my circuit board wallpaper for iOS 7…