2012年3月30日金曜日

NSString定数の定義


Cocoaから事例をひとつ取り上げてみます。
ヘッダファイルNSBundle.hに次の行があります。

FOUNDATION_EXPORT NSString * const NSLoadedClasses;

FOUNDATION_EXPORTはobjc_runtime.hでマクロ定義されており、Objective-Cの場合はexternとなります。(実際は#ifで場合分けされています。)

#define FOUNDATION_EXTERN extern
#define FOUNDATION_EXPORT  FOUNDATION_EXTERN

上記の例の場合はNSBundle.mで変数NSLoadedClassesが宣言、定数設定されます。

NSString * const NSLoadedClasses=@"NSLoadedClasses";

これにより、NSBundle.hをimportすると外部定義変数としてNSLoadedClassesが参照解決できるようになり、コンパイルするとNSBundle.mで宣言されている@"NSLoadedClasses"が値となります。

これに倣って自分で使う場合は、.hファイルで外部変数の宣言をし、.mで変数宣言、定数設定をします。

SomeClass.h
    extern NSString * const SomeConstant;
    @interface SomeClass
    @end

SomeClass.m
    NSString * const  SomeConstant = @"SomeConstValue";
    @implements SomeClass
    @end

利用するクラスでSomeClass.hをimportします。

#import "SomeClass.h"

NSStringの文字列を比較する場合は通常はisEqualsToStringを使いますが、定義済みの定数どうしであれば==で比較することができます。

参考: Constants in Objective C


#defineによる定数定義

もうひとつの文字列定数の定義に#defineマクロを使う方法があります。
#define SOME_WORD @"SomeWordString"

サンプルアプリのReachability.hでは次のようなマクロによる定義が使われています。
#define kReachabilityChangedNotification
@"kNetworkReachabilityChangedNotification"

実はconstを使った定義と#defineでは結果は同じです。
次のようなコードを書きます。

MyClass.h
#define SOME_WORD @"SomeWordString"
extern NSString * const someWord;

MyClass.m
NSString * const someWord = @"SomeWordString";

そして次のように各々のアドレスを調べてみると、どちらも同一のオブジェクトであることがわかります。
NSLog(@"SOME_WORD = %p", SOME_WORD);
NSLog(@"someWord = %p", someWord);
NSLog(@"(SOME_WORD == someWord) = %d", (SOME_WORD == someWord));

これはソース中に@""で直書きされたNSStringはconstとして扱われ、constについては同一の値のものはひとつだけデータ領域に確保されるためです。

重要な違いは、同一名のマクロを別の値で再定義するとエラーにはならず、コンパイラがマクロを解釈する時点で最後に定義されたものが有効になり、一方constを使った場合はグローバル変数で同一名の変数が二度宣言されるとエラーになるということです。そのため、#defineで同一名のマクロを異なる値で再定義すると、ソース中は同じSOME_WORDでも別オブジェクトを指すことになります。

また、マクロの場合はプリプロセッサで文字列置き換えを行うものなので、大量にあると若干コンパイル時のオーバーヘッドになるかもしれません(気付くほどではないかもしれませんが)。

Cocoaでは概ね次のような場合に#defineによる定義が使用されているようです。
・同一ファイルまたはクラス、ないしは限定的な範囲で使用されるもの
・数値の場合(NSIntegerなどオブジェクトでないもの、BOOL含む)

ちなみに、Cocoaでは基本的に#defineを使う場合はSOME_WORDのように全て大文字で文字区切りを"_"とし、constを使う場合はPrefix+単語の先頭大文字+小文字(例:MYSomeWord)となっています。ただし、#defineで数値定数を定義する場合は後者と同じ命名規則になっているものも多く見かけます。

typedefと併用するとより定数らしくなります。

SomeClass.h
typedef NSString* SomeStringType;
extern SomeStringType const strType1;

SomeClass.m
SomeStringType const strType1 = @"strType1";

定数として定義されたNSStringであれば、isEqualToSting:ではなく、==で一致判断が行えます。


関連Blog
NSString定数の定義
数値定数の定義

0 件のコメント: