Friday, 16 August 2013

Defining Constants in Objective-C

One of the nice things about working in developer relations is I get exposed to an awful lot of code written by an awful lot of different people, which means I regularly encounter techniques I haven't seen before. I wanted to quickly post on one that is possibly very familiar to iOS developers, but that I only saw recently - though I have to say that for readability's sake I would recommend some good commenting if you do decide to use it!

The code came from an iOS codelab for the excellent Google Play Games service, written by the mighty Kerp. The code was a header file doing something pretty normal: defining a list of constants for use in game. What was a little unusual in that it had the values both declared and defined in the header, and that there seemed to be some business with macros occurring.

This took me a little bit of parsing! In general the normal way of defining these sorts of constants in Objective-C is to have an extern'd definition in the header file, and the value in the implementation (.m) file, so:

This is assuming that you need the constant available throughout your app - if its just needed in one file you can declare it static in the .m:

It's also possible to use the preprocesser to #define a constant, but in general it's nicer to have a real value. This particularly true for strings, as it will mean all references to the constant will be to the exact same bit of memory, as opposed to several bits of memory that happen to have the same contents.

So, back to our mystery macros. The problem they are solving is around having both the declaration and the value in the same place. The .h file with the constants in is imported into all the .m files that need it. The #import directive in objective-c works very much the regular C include - in that it is replaced by the contents of the file it references. However, #import will only do this once per compilation unit - effectively per .m - so if you #import files a and b, and b imports a again, you wont run into trouble.

That said, each compilation unit does the inclusion separately, and here is where the macros kick in. If I include my constants file in, say, a ViewController, then the macros at the top are processed:

  • We check to see if APP_DEFINE_CONSTANTS is defined (it isn't)
  • We set EXTERN to "extern"
  • We set INITIALIZE_AS to nothing
  • We include the line in our file: extern const double kAppFirstConstant

Exactly as we were using in the regular .m and .h separation! Now, as you may have guessed we can look at the Constants.m file, which just contains:

When we compile that unit, the same code will get inlined, but this time:

  • We check to see if APP_DEFINE_CONSTANTS is defined (it is)
  • We set EXTERN to nothing
  • We set INITIALIZE_AS to "=x
  • We include the line in our file: const double kAppFirstConstant = 0.025

Exactly what we'd want in our .m file! So we have both declarations and values in one file. As an aside, if you're wondering why the constants start with k, it's because that's the Google C++ coding style suggests that, and is referenced in the Google Objective-C coding style. It presumably found its way into the guides as a vestigial bit of Hungarian Notation.