昔々読んだ本(リファクタリング)にNullObjectパターンなるものが紹介されていて、その中でのオブジェクトがnullオブジェクトかの確認方法はisNullだった。rubyでもnilかどうかの判定はobj.nil?、ものすごく分かりやすい。
Objective-CでのNSNullオブジェクトかの比較方法は下記の通り。ちょっとだけだけど冗長な感じがする。
-(BOOL)test:(NSNumber*)value {
if( value == nil || [value isEqual:[NSNull null]] ) {
return NO;
}
return YES;
}
nilの比較は100歩譲るとして、[value isNull]くらいできても良いと思うんだ.
という訳で実現してみた。
※ 職場では、環境の根底を勝手に変更すると混乱すること必至なので、関係者からの同意を得てからにしましょう。
NSObjectにisNullを追加する
NSObjectにカテゴリを追加してisNullメソッドを追加します。
NSObject+isNull.h
@interface NSObject (isNull)
-(BOOL)isNull;
@end
NSObject+isNull.m
#import "NSObject+isNull.h"
@implementation NSObject (isNull)
-(BOOL)isNull {
return [[NSNull null] isEqual:self];
}
@end
プリコンパイルヘッダにNSObject+isNull.hを追加する.
デフォルトではプリコンパイルヘッダが有効になっていると思うので、pchファイルにNSObject+isNull.hを追加します。これでプロジェクト内の全てのソースファイルから、NSObject+isNull.hがインクルードされます。ソースファイル毎に#import "NSObject+isNull.h"する必要がなくなります。
プロジェクト名-Prefix.pch
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "NSObject+isNull.h"
#endif
動作確認
これで次のようにかけるようになりました。
-(BOOL)test:(NSNumber*)value {
if( value == nil || [value isNull] ) {
return NO;
}
return YES;
}
…value == nil、邪魔だなー。
ここまで書いてだけど、[NSNull isNull:value]の方が良い気がしてきた.
書き直します。
[NSNull isNull]
// NSNull+isNull.h
#import <Foundation/Foundation.h>
@interface NSNull (isNull)
+(BOOL)isNull:(id)obj;
@end
// NSNull+isNull.m
import "NSNull+isNull.h"
@implementation NSNull (isNull)
+(BOOL)isNull:(id)obj {
return obj == nil || [[NSNull null] isEqual:obj];
}
@end
// プロジェクト名.pch
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "NSNull+isNull.h"
#endif
動作確認
-(BOOL)test:(NSNumber*)value {
if([NSNull isNull:value]) {
return NO;
}
return YES;
}
余談
あまり意味がない、言葉遊びのように感じられるかもしれませんが、
この変更により以下のようなメリットがあると思います。
- コードの意図が明確になった
- value = nilのようなミスをしなくてすむ
- 「nilでない場合」が書きやすくなった(![NSNull isNull:value]ですむ。)
- nilか[NSNull null]か気にしなくてよくなった