What did I just compile?

In most projects you'll reach a point where you want to run slightly different code depending on the type of build configuration you are using. You may want to only use logging in DEBUG or potentially use different API keys for services between ad hocs and store builds. The process of doing this is fairly straight forward and uninteresting but I'm just going to point out how the pieces fit together and a couple of ways to test your setup to give you the confidence that the correct code will go to the app store and you don't end up with egg on your face.

Let's say we want to remove all occurrences of NSLog() in our project for any build configuration that is not DEBUG - simple enough.

Prefix.pch

#ifndef DEBUG
#  undef  NSLog
#  define NSLog(...)
#endif

#ifndef?

So the first line #ifndef DEBUG is a compiler directive to check to ensure that the token DEBUG has not been defined in this file or any included files. If DEBUG has been defined then it removes the next two lines before compilation. If DEBUG has not been defined then these lines will be compiled and have the effect of replacing all calls to NSLog() with nothing. If we want to check that a token has been defined we use the similar looking #ifdef.

So hopefully the above was nothing new or interesting but you may be wondering where and why would DEBUG be defined as I don't do it manually?


Where's my define?

This is where the Xcode templates help us out a little. Jump to the build settings of a project and search for 'preprocessor macros' and you'll see something like this:

Preprocessor Macros

As you can see from the above screenshot the Xcode default project settings have the define DEBUG=1 declared but only for the debug configuration.

There is nothing special about how this is done that prevents you from adding your own defines. For example you may want to augment this so you can check other configurations. In the screenshot below I add RELEASE=1 for release builds and ADHOC=1 for a new configuration made for ad hocs.

Preprocessor Macros


Location, location, location

So were would you put some code to remove NSLog()? It needs to be available everywhere to ensure that it effects every NSLog. A common place could be to just slap it in the {Project}-Prefix.pch file. This file will have been automatically created for you when starting with one of the Xcode templates.

I used the term "just slap it in the {Project}-Prefix.pch" as this is the simplest way to get code included everywhere in your project, but it's not pretty. If you have too much code that you want to use everywhere your .pch will start to look like a dumping ground and it's probably time to move this stuff into a better named file and include that in the .pch. Yes I know this is just hiding the code in another file but it certainly seems like a good compromise of convenience and tidiness, the alternative being to #import your new file everywhere you want to use your new code.

Slight detour

If you want to rename the .pch you have to also ensure that you update the build settings to reference the new name. The quickest way to find the setting is to use the filter on the build settings tab of the project and look for "prefix header":

Image


Rocky waters

The .pch suffix actually stands for pre compiled header and it's precompiled to speed up your build times. This means that Xcode can get a little bit moody if anything changes in any of the imported files - for that reason it's best to only #import stable things (things that don't change often) into the .pch. If you get into a situation where your project builds but Xcode spits out warnings related to the files that are #import'd into your .pch you have to do a little dance to get Xcode working again.

I generally follow these steps - I build at each point and if the warnings go away you are done, if not keep going through the list

  1. Do a full clean (⌘⌥⇧K)
  2. Comment out the offending import, build, then uncomment the import, build
  3. Do step 1 again and also delete derived data
  4. Close Xcode
  5. If you got here you are having a bad day :(

Test

Great so you are using different build configurations to remove or add code but how do you get the confidence to know that you are not about to release the wrong code to the app store?

Ad hoc

You should definitely without fail make an ad hoc using the configuration that you will use when going to store. To do this go to Product > Scheme > Edit Scheme (⌘<), switch to Archive and ensure that the Build Configuration is set to your app store configuration (most likely Release).

Release config

Now just test your app and make sure that it behaves as you expected.

Preprocess

If you are like me and want to go full belts and braces you'll want to see the code that will be run (seeing is believing). To do this edit the Run settings go to Product > Scheme > Edit Scheme (⌘<), switch to Run and ensure that the Build Configuration is set to your app store configuration (most likely Release).

Run config

Now with the file that contains the conditional open go to Product > Perform Action > Preprocess "MyFile.m"

Preprocess

Xcode will make itself busy and then spit out the preprocessed file, which you can now search to ensure that the correct code was included/excluded. ProTip: your code will be way way way down the file, right near the bottom.

Wrap up

I'm not going to lie this was not a very interesting post but it's important to know how this stuff works to make sure that you are not accidentally including bad code in production builds.

Believe it or not there was a couple of useful/interesting things in the above post:

  • Looked at 2 build settings Preprocessor Macros and Prefix Header
  • Learn the Xcode dance when the .pch gets itself in knots
  • Test your code to be confident that the #ifdef's are working as advertised

I think the ways of testing are the most important take away as it's never a good idea to trust the code you just copied and pasted from StackOverflow. Your project may be configured differently and it would be foolish to trust a computer to "just work".

How does it work - Bit shifting & masking

tl;dr

I explore how bitwise manipulations allow you to store the red, green, blue and alpha components of a colour in a single integer.


Today I'm going to step through a helper method that allows you to create a UIColor instance from the hex colour values that designers love to give you. The category method in question looks like this:

+ (UIColor *)pas_colorWithRGBAHex:(u_int32_t)rgba;

When I first saw an implementation of this many years ago I remember staring at the source and feeling my eyes glaze over. Let's break this apart and demystify what's really going on.


u_int32_t

One of the questions you might ask when looking at this method is why be so specific with the type of the parameter and how do we know it needs to be a u_int32_t and not something else like u_int64_t or int? To answer that we'll look at some sample input provided by a designer in the form of a lovely green colour.

#01A902FF /* a lovely green */

This is actually 4 pairs of hexadecimal numbers, reading from left to right we have:

  • The Red component is #01
  • The Green component is #A9
  • The Blue component is #02
  • The Alpha component is #FF

In base 10, our normal counting system, this would be 1, 169, 2, 255 respectively.

Each hexadecimal component covers the range 00..FF, which is equivalent to 0..255. To be able to represent all the numbers from 0..255 in binary we would need 8 bits.

bits

A bit can only represent 21 values as it can either be 1 or 0. By looking at groups of bits we can increase the number of values that can be represented. For example a 4 bit number can be configured in 24 or 2 x 2 x 2 x 2 different combinations, which means we can use it to represent 16 unique values.

To represent 0..255 we would need 8 bits as 28 or 2 x 2 x 2 x 2 x 2 x 2 x 2 x 2 equals 256.

So knowing that we have to represent 4 colour components that each require 8 bits we can finally understand why we chose u_int32_t as the type of the argument (4 x 8 = 32).


rgba

The next logical question is how do we extract the four 8 bit values from a single integer?

Let's start by looking at how our hexadecimal #01A902FF (green) value actually looks under the hood when represented by a u_int32_t. Keep in mind that a u_int32_t is just a collection of 32 bits that can each have a value of 1 or 0:

1101010010000001011111111 // binary solo (Flight of the Conchords reference)

Let's do that again but with some spacing and some markers to show which bits represent our RGBA components.

+--  R  --+--  G  --+--  B  --+--  A  --+
|         |         |         |         |
 0000 0001 1010 1001 0000 0010 1111 1111

When a bit is set to 1 it means that the unit that the bit corresponds to should be included in the total. The units are powers of 2, with the power to raise by incrementing by 1 each time and starting from the right with 20. Here's a few examples using just the first 8 bits:

| 2^7 | 2^6 | 2^5 | 2^4 | 2^3 | 2^2 | 2^1 | 2^0 |
   1     1     1     1     1     1     1     1    = 255 = (128  + 64 + 32 + 16 + 8 + 4 + 2 + 1)
   0     1     0     0     0     0     0     0    = 64  = (64)
   0     0     1     0     1     0     1     0    = 42  = (32 + 8 + 2)

With that slight detour out of the way we can crack on with extracting the Alpha component, which is the easiest to work with. Currently our u_int32_t is capable of representing the values 0..4294967295, but we only want to extract an 8 bit value with a range of 0..255. We need to somehow look at just the 8 bits that we care about - this can be achieved with bit masking.


&

The logical AND, which is represented by the single ampersand character (&), is used to compare two values by looking at the bits that the values are represented by. When using the logical AND a bit is only set to 1 in the resulting value if it is 1 in both the left and right values used in an expression.

15 is represented by the binary 0b1111. So 15 & 15 would equal 15 and look like this:

0b1111   /*
0b1111 &  * All the bits are on in both values so all
------    * of the bits in the resulting value are on
0b1111    */

0 is represented by the binary 0b0000. So 0 & 15 would equal 0 and look like this:

0b0000   /*
0b1111 &  * There are no bits that are on in both values
------    * so the resulting value has no bits on
0b0000    */

10 is represented by the binary 0b1010 and 12 looks like 0b1100. So 10 & 12 would result in 8 and look like this:

0b1010   /*
0b1100 &  * Only the 8 bit is on in both values so only
------    * the 8 bit is on in the result
0b1000    */

This may not seem very helpful but this is the key to being able to treat our 32 bit value as if it was only 8 bits. To get our Alpha component we need to turn of the 24 bits that we don't care about and ensure that the first 8 bits keep their current values.

To achieve this we use a bit pattern that turns on only the first 8 bits and then logical AND this with our RGBA value:

rgba & #000000FF

#000000FF is hex for 255, which is the first 8 bits turned on (0b1111 1111) - here is how the above looks at the bit level:

+--  R  --+--  G  --+--  B  --+--  A  --+
|         |         |         |         |
 0000 0001 1010 1001 0000 0010 1111 1111
 0000 0000 0000 0000 0000 0000 1111 1111  &
 ---------------------------------------
 0000 0000 0000 0000 0000 0000 1111 1111

Great this has masked out the last 24 bits of our u_int32_t and has given us the result of 255 for the Alpha component.

Hopefully this makes sense and we are ready to try and extract more components. Let's jump to the Green value for a challenge. With our new technique we may naively assume that we just want to mask out all of the data that we don't want. We could do that with the following:

rgba & #00FF0000

#00FF0000 is the same as 0b0000 0000 1111 1111 0000 0000 0000 0000 - at the bit level we get:

+--  R  --+--  G  --+--  B  --+--  A  --+
|         |         |         |         |
 0000 0001 1010 1001 0000 0010 1111 1111
 0000 0000 1111 1111 0000 0000 0000 0000  &
 ---------------------------------------
 0000 0000 1010 1001 0000 0000 0000 0000 

This has indeed masked out the data we don't care about, but the resulting value is 11075584 which is well beyond the 0..255 range we was looking for. This is because the bits that are set to 1 represent 65536 + 524288 + 2097152 + 8388608.

What we need to do is shift the data to the right so that the bits representing the Green component move from their current place, in the middle, all the way over to the right so that they are in the first 8 bits of our variable. If the Green bit pattern (1010 1001) occurred in the first 8 bits we would get 1 + 8 + 32 + 128 = 169.

We move the values to the right with the right shift operator.


>>

We want to shift the 8 bits that represent the Green component all the way to the right. There are 16 bits in front of the Green component so we need to shift our number 16 times to the right - simple

rgba >> 16

As the bits are shifted to the right they will fall of the right hand side, then new values will need to be added to the left to pad the newly created space. What the padding values are depends on the variable's type and a few other things. After doing rgba >> 16 our bits now look like this

+------  new  ------+--  R  --+--  G  --+
|                   |         |         |
 0000 0000 0000 0000 0000 0000 1010 1001

This has gotten the bits of the Green component into a position where we can just mask them off like we did with the Alpha component.


If we follow this logic for each component we end up with the final demystified implementation of:

+ (UIColor *)pas_colorWithRGBAHex:(u_int32_t)rgba;
{
    int r = 0xFF & (rgba >> 24);
    int g = 0xFF & (rgba >> 16);
    int b = 0xFF & (rgba >> 8);
    int a = 0xFF & (rgba);

    return [UIColor colorWithRed:r / 255.0f
                           green:g / 255.0f
                            blue:b / 255.0f
                           alpha:a / 255.0f];
}

For each component we follow the pattern of:

  • Shift the bits in the rgba variable to the right until the data we are interested in is in the first 8 bits
  • Mask of the first 8 bits to ensure that any other bits are ignored

This category method requires the hex value to be defined in the form of 2 hexadecimal numbers for each colour component - you could easily make different methods to handle different formats now you know how it all works. This category method would be used like this:

UIColor *greenColor = [UIColor pas_colorWithRGBAHex:0x01A902FF];

Wrap up

This post only looks at the right shift >> and logical AND & operators but hopefully it allows you to begin to imagine in your minds eye how bits are stored and can be manipulated.

Essentially under the hood the u_int32_t variable is just held as a sequence of bits. We are exploiting this structure to encode more than one piece of information in a single variable. We then have to use various bitwise operations to extract the meaning that we encoded into the integer.

How does it work - Meta macros

tl;dr

Meta macros are pretty nifty but trying to follow how they work can really challenge the limits of your mental stack frame.


Today I'm going to try and follow macro expansion from start to finish using libextobjc to do some basic metaprogramming. This is going to be a bumpy ride but as always it's great to see how different techniques can be used to solve problems.

Let's start with a fictitious problem that I would like to solve with some metaprogramming. I would probably never do this in a real project but it gives me a realistic use case to work through.

Imagine I want my view controllers to fail hard if I forget to connect up an outlet in the xib file. I could start with something like this:

- (void)viewDidLoad;
{
  [super viewDidLoad];
  
  NSParameterAssert(self.firstNameTextField);
  NSParameterAssert(self.lastNameTextField);
  NSParameterAssert(self.passwordTextField);
  NSParameterAssert(self.passwordConfirmationTextField);
}

whoa that's a lot of repetition and it's not going to scale well. What would be great is if I could write some code that would write this repetitious code for me, ideally I would just type something like this:

- (void)viewDidLoad;
{
  [super viewDidLoad];
  
  PASAssertConnections(firstNameTextField, lastNameTextField, passwordTextField, passwordConfirmation);
}

This seems a lot DRY'er so let's aim for something similar to this and see how we get on.


Down the rabbit hole we go

metamacro_foreach

After examining the metamacros header I can see that there is a foreach macro that sounds like it would be perfect for this task.

The definition of metamacro_foreach looks like this:

#define metamacro_foreach(MACRO, SEP, ...)

After reading the docs I can see that the MACRO argument should be the name of a macro that takes two arguments in the form of MACRO(INDEX, ARG). The INDEX parameter will be the index of the current iteration in the for loop and the ARG parameter will be the argument for the current iteration in the for loop.

So I need to start of by defining a macro that takes these two arguments and expands to the NSParameterAssert that I want. Here's a first stab at such a macro

#define OUTLET_ASSERT(INDEX, NAME) NSParameterAssert([self NAME])

I don't actually care to use the value of INDEX so it is ignored. This is the macro that will be used within the metamacro_foreach and will eventually expand into the required NSParameterAsserts.

In each of the following examples I'll show the input (starting macro) above the 3 dashes and what this would theoretically expand into below the 3 dashes. I'll optionally show any macro definitions at the top of the code block.

Here's how my OUTLET_ASSERT macro will work:

OUTLET_ASSERT(0, firstNameTextField);

---

NSParameterAssert([self firstNameTextField]);

metamacro_foreach

Now let's see how we can use metamacro_foreach to write the PASAssertConnections macro that will take in a list of ivar names and expand them to the required NSParameterAsserts.

#define metamacro_foreach(MACRO, SEP, ...) \
        metamacro_foreach_cxt(metamacro_foreach_iter, SEP, MACRO, __VA_ARGS__)
        
metamacro_foreach(OUTLET_ASSERT, ;, firstNameTextField, lastNameTextField)

---

metamacro_foreach_cxt(metamacro_foreach_iter, ;, OUTLET_ASSERT, firstNameTextField, lastNameTextField)

In this case I pass OUTLET_ASSERT as the macro to use on each iteration. I pass ; to use as a separator between iterations, which will terminate each NSParameterAssert. Then finally a comma separated list of ivar names that we are going to iterate over and generate the NSParameterAsserts for.

With the previous expansion there are now two new macros that we need to look up and understand metamacro_foreach_cxt and metamacro_foreach_iter. metamacro_foreach_iter is arguably the simpler of the two but it's not needed until the end so let's see how metamacro_foreach_cxt expands.


metamacro_foreach_cxt

#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
        metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)

metamacro_foreach_cxt(metamacro_foreach_iter, ;, OUTLET_ASSERT, firstNameTextField, lastNameTextField)
        
---
        
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(firstNameTextField, lastNameTextField))(metamacro_foreach_iter, ;, OUTLET_ASSERT, firstNameTextField, lastNameTextField)

Great when this macro expands it introduces 2 more macros to look up, metamacro_concat and metamacro_argcount.

metamacro_concat is the easier of the two so we'll take a look at that first.


metamacro_concat

#define metamacro_concat(A, B) \
        metamacro_concat_(A, B)
        
#define metamacro_concat_(A, B) A ## B
        
metamacro_concat(metamacro_foreach_cxt, 2)

---

metamacro_foreach_cxt2

Cool so metamacro_concat just expands to metamacro_concat_, which then just joins the tokens together using ##. So metamacro_concat just has the effect of joining it's two arguments into one string.

Now we need to jump back to see how metamacro_argcount works


metamacro_argcount

#define metamacro_argcount(...) \
        metamacro_at(20, __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
        
metamacro_argcount(firstNameTextField, lastNameTextField)

---
        
metamacro_at(20, firstNameTextField, lastNameTextField , 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

The metamacro_argcount macro uses another macro called metamacro_at. The metamacro_at is similar in concept to indexing into an array like myArray[index]. In plain English this macro is the same as "give me the nth item in the following list".

The metamacro_argcount macro uses a clever little trick. If we put the numbers from INDEX down to 0 into an array and then ask for the value at INDEX we would get the last number, which would be 0. If we preprend something to the beginning of this array and asked for the value at INDEX again we would now get 1.

Let's see this in Objective-C so it's easier to picture:

NSInteger index = 3;

@[                           @3, @2, @1, @0 ][index]; //=> @0 - 0 args
@[ @"argument",              @3, @2, @1, @0 ][index]; //=> @1 - 1 arg preprended
@[ @"argument", @"argument", @3, @2, @1, @0 ][index]; //=> @2 - 2 args preprended

The relationship is that when you prepend an argument to the array you shift all of the numeric values to the right by one step, which moves a higher number into the index that is being fetched. This of course only works up to the value of INDEX - so we can tell that this particular implementation of metamacros only supports 20 arguments.

NB - this implementation of metamacros requires at least one argument to be given when using metamacro_argcount.

You'll see the trick of inserting __VA_ARGS__ into argument lists at different points used a few times so it's worth making sure you understand what is happening above.

Ok so that makes sense but what about metamacro_at?


metamacro_at

#define metamacro_at(N, ...) \
        metamacro_concat(metamacro_at, N)(__VA_ARGS__)

Great there's our old friend metamacro_concat so we don't need to look up how that works again to know that this will expand like this:

metamacro_at(20, firstNameTextField, lastNameTextField , 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

---

metamacro_at20(firstNameTextField, lastNameTextField , 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)

The change is very subtle. The 20 has moved from being an argument to now actually being part of the macro name. So now we need to look up metamacro_at20

#define metamacro_at20(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, ...) metamacro_head(__VA_ARGS__)

It turns out that there are variants of metamacro_at defined for 0 to 20, which allows you to access any of the first 20 arguments from the __VA_ARGS__ arguments.

This is another common trick you'll see with metamacros, at some point you have to knuckle down and write out multiple versions of the same macro to handle different length argument lists. You'll often see that metamacros are generated by other scripts that allow you to specify how many arguments you would like to support without having to hand roll all the variations of metamacro_at0..N.

To make metamacro_at a little easier to digest I'll examine one of the smaller versions of this macro.

#define metamacro_at2(_0, _1, ...) metamacro_head(__VA_ARGS__)

metamacro_at2(firstNameTextField, lastNameTextField, passwordTextField, passwordConfirmationTextField)

---

metamacro_head(passwordTextField, passwordConfirmationTextField)

The _0 and _1 arguments are basically used as placeholders to gobble up the items at indices 0 and 1 from the arguments. Then we bundle the rest of the arguments together with .... The newly trimmed __VA_ARGS__ is then passed into metamacro_head


metamacro_head

#define metamacro_head(...) \
        metamacro_head_(__VA_ARGS__, 0)

#define metamacro_head_(FIRST, ...) FIRST

metamacro_head(passwordTextField, passwordConfirmationTextField)

---

passwordTextField

metamacro_head uses the opposite trick to metamacro_at*. In this case we are only interested in the first item and we want to throw away the rest of the __VA_ARGS__ list. This is achieved by grabbing the first argument in FIRST and then collecting the rest with ....

Wow that escalated quickly. We now need to unwind out mental stack frame back to metamacro_foreach_cxt.


metamacro_foreach_cxt

Now we are more enlightened we can go back and expand both metamacro_concat and metamacro_argcount in the following:

#define metamacro_foreach_cxt(MACRO, SEP, CONTEXT, ...) \
        metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(__VA_ARGS__))(MACRO, SEP, CONTEXT, __VA_ARGS__)
        
metamacro_concat(metamacro_foreach_cxt, metamacro_argcount(firstNameTextField, lastNameTextField))(metamacro_foreach_iter, ;, OUTLET_ASSERT, firstNameTextField, lastNameTextField)

---

metamacro_foreach_cxt2(metamacro_foreach_iter, ;, OUTLET_ASSERT, firstNameTextField, lastNameTextField)

Don't worry the end is now very much in sight, just a couple more painless macro expansions. The previous expansion gives us the new metamacro_foreach_cxt2 macro to check out.


metamacro_foreach_cxt2

This is another example of macro that has multiple versions defined from 0..20. Each of these foreach macros works by utilising the foreach macro that is defined to take one less argument than itself until we get all the way down to metamacro_foreach_cxt1

#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
    metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
    SEP \
    MACRO(1, CONTEXT, _1)
    
metamacro_foreach_cxt2(metamacro_foreach_iter, ;, OUTLET_ASSERT, firstNameTextField, lastNameTextField)

We are now at the point where we need to see what MACRO expands to. In this case MACRO is actually the metamacro_foreach_iter macro that we passed in near the beginning and I delayed explaining.


metamacro_foreach_iter

This macro is really just an implementation detail and as such shouldn't be used directly but we still want to see what part it plays:

#define metamacro_foreach_iter(INDEX, MACRO, ARG) MACRO(INDEX, ARG)

metamacro_foreach_iter(0, OUTLET_ASSERT, firstNameTextField)

---

OUTLET_ASSERT(0, firstNameTextField)

Nice and simple - metamacro_foreach_iter is just a helper that takes our macro OUTLET_ASSERT and the two arguments that our macro should receive and puts the pieces in the right order to be further expanded into the NSParameterAssert calls that we want.

Thankfully that was only a minor detour so let's get right back to metamacro_foreach_cxt2


#define metamacro_foreach_cxt2(MACRO, SEP, CONTEXT, _0, _1) \
    metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) \
    SEP \
    MACRO(1, CONTEXT, _1)
    
metamacro_foreach_cxt2(metamacro_foreach_iter, ;, OUTLET_ASSERT, firstNameTextField, lastNameTextField)

---

metamacro_foreach_cxt1(metamacro_foreach_iter, ;, OUTLET_ASSERT, firstNameTextField) \
    ; \
    OUTLET_ASSERT(1, lastNameTextField)

If you have gotten this far then the above is nothing special so we can progress straight to the next step:

#define metamacro_foreach_cxt1(MACRO, SEP, CONTEXT, _0) MACRO(0, CONTEXT, _0)
    
metamacro_foreach_cxt1(metamacro_foreach_iter, ;, OUTLET_ASSERT, firstNameTextField) \
    ; \
    OUTLET_ASSERT(1, lastNameTextField)

---

    OUTLET_ASSERT(0, firstNameTextField) \
    ; \
    OUTLET_ASSERT(1, lastNameTextField)

And that's it - we've followed the metamacro_foreach macro from the beginning of it's use all the way to it's end expansion and hopefully our heads are still in one piece.

Wrapping up

At the beginning of the post I said I was aiming for

PASAssertConnections(firstNameTextField, lastNameTextField, passwordTextField, passwordConfirmation);

now I'm actually one step away from achieving this, but if this post has gotten your interest I'll leave that as a simple exercise - it's always better to learn by doing and not just skimming through blog posts hoping to learn by osmosis.

Metaprogramming is normally something that people associate with more dynamic languages like Ruby but there's a whole load of possibilities and cool tricks out there just waiting to be learned. As always I encourage you to join me in peeling back the curtain and seeing that there is normally no magic to be found in your favorite OSS projects.