Effective Objective-C 2.0: Item 4: Prefer Typed Constants to Preprocessor #define

原创 2013年12月02日 11:35:15

Item 4: Prefer Typed Constants to Preprocessor #define

曾经面试被被问到 Preprocessor #define 有什么缺点, 这篇文章很好解答了这个问题。 ^ ^

When writing code, you will often want to define a constant. For example, consider a UI view class that presents and dismisses itself using animations. A typical constant that you’d likely want to factor out is the animation duration. You’ve learned all about Objective-C and its C foundations, and so you take the approach of defining the constant like this:


This is a preprocessor directive; whenever the stringANIMATION_DURATION is found in your source code, it is replaced with 0.3. This might seem exactly what you want, but this definition has no type information. It is likely that something declared as a “duration” means that the value is related to time, but it’s not made explicit.Also, the preprocessor will blindly replace all occurrences ofANIMATION_DURATION, so if that were declared in a header file, anything else that imported that header would see the replacement done.

To solve these problems, you should make use of the compiler. There is always a better way to define a constant than using a preprocessor define. For example, the following defines a constant of typeNSTimeInterval:

static const NSTimeInterval kAnimationDuration = 0.3;

Note that with this style, there is type information, which is beneficial because it clearly defines what the constant is. The type isNSTimeInterval, and so it helps to document the use of that variable. If you have a lot of constants to define, this will certainly help you and other people who read the code later.

Also note how the constant is named. The usual convention for constants is to prefix with the letter k for constants that are local to a translation unit (implementation file). For constants that are exposed outside of a class, it is usual to prefix with the class name. Item 19 explains more about naming conventions.

It is important where you define your constants. Sometimes, it is tempting to declare preprocessor defines in header files, but that is extremely bad practice, especially if the defines are not named in such a way that they won’t clash. For example, the ANIMATION_DURATIONconstant would be a bad name to appear in a header file. It would be present in all other files that imported the header. Even the static const as it stands should not appear in a header file. Since Objective-C has no namespaces, it would declare a global variable calledkAnimationDuration. Its name should be prefixed with something that scopes it to the class it is to be used with, such asEOCViewClassAnimationDuration. Item 19 explains more about using a clear naming scheme.

A constant that does not need to be exposed to the outside world should be defined in the implementation file where it is used. For example, if the animation duration constant were used in a UIViewsubclass, for use in an iOS application that uses UIKit, it would look like this:

// EOCAnimatedView.h
#import <UIKit/UIKit.h>

@interface EOCAnimatedView : UIView
- (void)animate;

// EOCAnimatedView.m
#import "EOCAnimatedView.h"

static const NSTimeInterval kAnimationDuration = 0.3;

@implementation EOCAnimatedView
- (void)animate {
    [UIView animateWithDuration:kAnimationDuration
                         // Perform animations

It is important that the variable is declared as both static andconst. The const qualifier means that the compiler will throw an error if you try to alter the value. In this scenario, that’s exactly what is required. The value shouldn’t be allowed to change. The staticqualifier means that the variable is local to the translation unit in which it is defined. A translation unit is the input the compiler receives to generate one object file. In the case of Objective-C, this usually means that there is one translation unit per class: every implementation (.m) file. So in the preceding example,kAnimationDuration will be declared locally to the object file generated from EOCAnimatedView.m. If the variable were not declaredstatic, the compiler would create an external symbol for it. If another translation unit also declared a variable with the same name, the linker would throw an error with a message similar to this:

duplicate symbol _kAnimationDuration in:

In fact, when declaring the variable as both static and const, the compiler doesn’t end up creating a symbol at all but instead replaces occurrences just like a preprocessor define does. 

Remember, however, the benefit is that the type information is present.

Sometimes, you will want to expose a constant externally. For example, you might want to do this if your class will notify others using NSNotificationCenter. This works by one object posting notifications and others registering to receive them. Notifications have a string name, and this is what you might want to declare as an externally visible constant variable. Doing so means that anyone wanting to register to receive such notifications does not need to know the actual string name but can simply use the constant variable.

Such constants need to appear in the global symbol table to be used from outside the translation unit in which they are defined. Therefore, these constants need to be declared in a different way from thestatic const example. These constants should be defined like so:

// In the header file
extern NSString *const EOCStringConstant;

// In the implementation file
NSString *const EOCStringConstant = @"VALUE";

The constant is “declared” in the header file and “defined” in the implementation file. In the constant’s type, the placement of theconst qualifier is important. These definitions are read backward, meaning that in this case, EOCStringConstant is a “constant pointer to an NSString.” This is what we want; the constant should not be allowed to change to point to a different NSString object.

The extern keyword in the header tells the compiler what to do when it encounters the constant being used in a file that imports it. The keyword tells the compiler that there will be a symbol for EOCStringConstant in the global symbol table. 

This means that the constant can be used without the compiler’s being able to see the definition for it

The compiler simply knows that the constant will exist when the binary is linked.

The constant has to be defined once and only once. It is usually defined in the implementation file that relates to the header file in which it is declared. The compiler will allocate storage for the string in the data section of the object file that is generated from this implementation file. 

When this object file is linked with other object files to produce the final binary, the linker will be able to resolve the global symbol for EOCStringConstant wherever else it has been used.

The fact that the symbol appears in the global symbol table means that you should name such constants carefully. For example, a class that handles login for an application may have a notification that is fired after login has finished. The notification may look like this:

// EOCLoginManager.h
#import <Foundation/Foundation.h>

extern NSString *const EOCLoginManagerDidLoginNotification;

@interface EOCLoginManager : NSObject
- (void)login;

// EOCLoginManager.m
#import "EOCLoginManager.h"

NSString *const EOCLoginManagerDidLoginNotification =

@implementation EOCLoginManager

- (void)login {
    // Perform login asynchronously, then call 'p_didLogin'.

- (void)p_didLogin {
    [[NSNotificationCenter defaultCenter]


Note the name given to the constant. Prefixing with the class name that the constant relates to is prudent and will help you avoid potential clashes. This is common throughout the system frameworks as well. UIKit, for example, declares notification names as global constants in the same way. The names includeUIApplicationDidEnterBackgroundNotification andUIApplicationWillEnterForegroundNotification.

The same can be done with constants of other types. If the animation duration needed to be exposed outside of the EOCAnimatedView class in the preceding examples, you could declare it like so:

// EOCAnimatedView.h
extern const NSTimeInterval EOCAnimatedViewAnimationDuration;

// EOCAnimatedView.m
const NSTimeInterval EOCAnimatedViewAnimationDuration = 0.3;

Defining a constant in this way is much better than a preprocessor define because the compiler is used to ensure that the value cannot change. Once defined in EOCAnimatedView.m, that value is usedeverywhere. A preprocessor define could be redefined by mistake, meaning that different parts of an application end up using different values.

In conclusion, avoid using preprocessor defines for constants. Instead, use constants that are seen by the compiler, such as static const globals declared in implementation files.

Things to Remember

Image Avoid preprocessor defines. They don’t contain any type information and are simply a find and replace executed before compilation. They could be redefined without warning, yielding inconsistent values throughout an application.

Image Define translation-unit-specific constants within an implementation file as static const. These constants will not be exposed in the global symbol table, so their names do not need to be namespaced.

Image Define global constants as external in a header file, and define them in the associated implementation file. These constants will appear in the global symbol table, so 

Effective Objective-C 2.0 读书笔记

第 1 章 熟悉 Objective-C 第 2 章 对象消息运行时 第 3 章 接口和 API 设计 第 4 章 协议与分类 第 5 章 内存管理 循环引用 普通的两个变量互相引用 Block 循环...
  • xsl_bj
  • xsl_bj
  • 2016年06月06日 12:27
  • 4796

编写高质量的iOS代码--Effective Objective-C 2.0 读书笔记

编写高质量的iOS代码--Effective Objective-C 2.0 读书笔记 这本书年初刷完,感觉不错,介绍了很多小点,都是平日不怎么关注的. 第1章 熟悉Objective-C...
  • uxyheaven
  • uxyheaven
  • 2014年12月26日 23:56
  • 5086

阅读《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》总结

第1条:了解Objective-C语言的起源 Objective-C为C语言添加了面向对象特性,是其超集。Objective-C使用动态绑定的消息结构,也就是说,在运行时才会检查对象类型。接收一...
  • caojengineer
  • caojengineer
  • 2015年06月07日 22:52
  • 1233

Effective Objective-C 2.0: Item 42: Prefer GCD to performSelector and Friends

Item 42: Prefer GCD to performSelector and Friends Thanks to the extremely dynamic nature of Obje...
  • chuanyituoku
  • chuanyituoku
  • 2013年12月12日 21:32
  • 1816

Effective Objective-C 2.0:Item 48: Prefer Block Enumeration to for Loops

Item 48: Prefer Block Enumeration to for Loops Enumerating a collection is a very common task in ...
  • chuanyituoku
  • chuanyituoku
  • 2013年12月14日 23:29
  • 1270

Effective Objective-C 2.0: Item 41: Prefer Dispatch Queues to Locks for Synchronization

Item 41: Prefer Dispatch Queues to Locks for Synchronization Sometimes in Objective-C, you will com...
  • chuanyituoku
  • chuanyituoku
  • 2013年12月12日 21:14
  • 4854

《Effective Objective-C 2.0编写高质量iOS与OS X代码的52个有效方法》读书笔记(下)

1.为避免在不经意间使用了无效对象,一般在release之后会清空指针,=nil; 2.通常利用弱引用或者“手动”解除引用的方式破坏循环引用。 3.ARC下,规定以alloc、new、copy、mut...
  • zhaochen_009
  • zhaochen_009
  • 2017年04月06日 10:44
  • 349


《Delphi下深入Windows核心编程》(附录A Delphi编译指令说明)Delphi快速高小的编译器主要来自Object PASCAL的严谨,使用Delphi随时都在与编译器交流,大部分情况下...
  • xiaotao2004
  • xiaotao2004
  • 2006年09月13日 13:57
  • 1550

《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》--读书笔记

个人觉得这本书很不错!里面有很多很实用但以前没有太注意的地方,所以想纪录下来,当作自己的读书笔记吧。 熟悉Objective-C 1)Objective-C语言使用“消息结构”而不是“函数调用”。O...
  • weimeng809
  • weimeng809
  • 2016年01月11日 00:18
  • 603

《Effective Objective-C 2.0编写高质量iOS与OS X代码的52个有效方法》读书笔记(上)

1.OC对象所占内存总是分配在"堆"中,而绝不会分配在"栈"中,不能再栈中分配OC对象。"栈"中对象借助栈帧进行维护,"堆"中对象的管理借助引用计数机制. -(NSMutableArray *)te...
  • zhaochen_009
  • zhaochen_009
  • 2017年04月05日 10:10
  • 311
您举报文章:Effective Objective-C 2.0: Item 4: Prefer Typed Constants to Preprocessor #define