iosdev

Masterclass: Constants and Flags

You have probably seen something like this many times:

#define kMySegueIdentifier @"SegueMain"
#define reuseIdentifier @"TABLECELL"
#define FONT_SIZE 14.0f
#define CELL_CONTENT_WIDTH 320.0f
#define CELL_CONTENT_MARGIN 10.0f

CGSize size = [text sizeWithFont:[UIFont systemFontOfSize:FONT_SIZE] constrainedToSize:constraint lineBreakMode:UILineBreakModeWordWrap];

This is usually found in quick&dirty code samples, to define constants. You can also see it in many older APIs, where various constants were defined this way. This is bad. It works, but it’s lazy.

#define is pre-processor directive and should not be used to define numbers and strings that will then be used as part of the actual code. It’s a hard-coded value that lives in its own memory space, outside of your regular scope.

Use proper, modern stuff that Objective-C offers and Xcode supports very well.

Constants

To define a global constant for, say a notification name, do it like this in .h file (above @interface part):

extern NSString * const RDMDataManagerDidBecomeReadyNotification;

and then in .m file (also at the top, above interface/implementation):

NSString * const RDMDataManagerDidBecomeReadyNotification = @"RDMDataManagerDidBecomeReadyNotification";

Works great, it’s pretty clear, especially with very elaborate prefix that namespaces the notification to my DataManager class.

If you have way too many of these constants, the repetitiveness can become boring to type and seriously hurt code readability. In that case I recommend to use this neat way to struct them together (and even more clearly namespace them). In .h file:

extern const struct RTFilterCategoryStruct {
	__unsafe_unretained NSString *Language;
	__unsafe_unretained NSString *Country;
	__unsafe_unretained NSString *Condition;
} RTFilterCategory;

and in .m file:

const struct RTFilterCategoryStruct RTFilterCategory = {
	.Language = @"LANGUAGE",
	.Country = @"COUNTRY",
	.Condition = @"CONDITION",
};

To reference these strings in code, you then use:

cat.categoryCode = RTFilterCategory.Language;

Enumerations

An outdated way to create an enumeration is this:

enum {
    UITableViewCellStyleDefault,
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};

This is not good since it does not even hint what those values actually are. You can read more at NSHipster but modern way to write the stuff above is:

typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
    UITableViewCellStyleDefault,	
    UITableViewCellStyleValue1,
    UITableViewCellStyleValue2,
    UITableViewCellStyleSubtitle
};

Now it’s obvious that values are integers. What’s even better is that you can define the values as you want (if you don’t, first option is 0 and the rest is incremented by 1):

typedef NS_ENUM(NSInteger, MyTableViewCellStyle) {
    MyTableViewCellStyleDefault = 1,	
    MyTableViewCellStyleValue1 = 2,
    MyTableViewCellStyleValue2 = 4,
    MyTableViewCellStyleSubtitle = 8
};

You can also define just a starting value and then leave the rest to increment by one. You can reset the current value to anything, at any point. This is my favorite way to define constants for cells in multi-sectioned table view:

typedef NS_ENUM(NSInteger, RTSearchItem) {
	//	filters section
	RTSearchItemFiltersGrid = 0,	
	RTSearchItemFiltersButton,
	RTSearchItemFiltersPicker,
	RTSearchItemFiltersCOUNT,

	//	calendar section
	RTSearchItemCalendarDaysGrid = 0,
	RTSearchItemCalendarButton,
	RTSearchItemCalendarPicker,
	RTSearchItemCalendarCOUNT
};

This way section starts are recognizable and you can re-order your items between section by simply moving enum options around.

Enumerations are great when you need a clear, well-defined list of mutually exclusive options. But what if you need options that can be combined?

Flags

In Xcode, chose File/Open Quickly… in the menu – or use shortcut Cmd Shift O – and type UIView.h. Among many other enumerations in that header file, you will see this:

typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
    UIViewAutoresizingNone                 = 0,
    UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
    UIViewAutoresizingFlexibleWidth        = 1 << 1,
    UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
    UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
    UIViewAutoresizingFlexibleHeight       = 1 << 4,
    UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};

I assume you already know how these options are used:

view.autoresizingMask = UIViewAutoresizingFlexibleWidth 
	| UIViewAutoresizingFlexibleHeight 
	| UIViewAutoresizingFlexibleBottomMargin;

Using bit-shifting in the definition, you not only define a set of distinct values on their own but any combination of them – made using bitwise OR (|) – will also be distinct.

Inside your method, you then use bitwise AND (&) to check what options are set:

if (flags & UIViewAutoresizingFlexibleRightMargin) {

}

If you want to remove a specific option:

flags &= ~UIViewAutoresizingFlexibleRightMargin;

One great usage of this is, again, to increase code readability as well as simplicity. Say I have a method that takes JSON array returned from some API and I now need to parse the object, but I need to be able to specify what operations should be done regarding the already existing objects: insert new, delete obsolete, update existing.

One way is to write a method like this:

- (void)processResults:(NSArray *)jsonResults
   shouldInsertNew:(BOOL)insert
   shouldDeleteObsolete:(BOOL)delete
   shouldUpdateExisting:(BOOL)update
{
…
}

[self processResults:response 
   shouldInsertNew:YES 
   shouldDeleteObsolete:NO
   shouldUpdateExisting:YES];

With flags, it’s pretty neat to just do this:

- (void)processResults:(NSArray *)jsonResults
   actions:(RTProcessAction)actions
{
…
}

RTProcessAction actions = RTProcessActionInsert | RTProcessActionUpdate;
[self processResults:response actions:actions];

The true value of this approach reveals itself when you need to add more actions at some moment. Your method signature remains the same, all possible places that call the method are thus un-affected. Your existing code inside the method also stays the same as it was; you simply add new if (actions & RTProcessActionXX) block and work done.