HOME » Natsu note » iOS » [iOS][Cocoa] setValue:forUndefinedKey:で未定義キーを検出

[iOS][Cocoa] setValue:forUndefinedKey:で未定義キーを検出 2011/10/13/|iOS, ,

今回はちょっとしたデバッグテクニックをご紹介。

非常に便利なKVC(Key-Value Coding)ですが、ときにはデバッグがしにくいこともあります。keyは文字列なので、コード上や読み込む外部データなどにタイプミスがあってもエラーとしては検出されません。

setValue:forKey:でキーに未定義のキーを渡してしまうとクラッシュしてしまいますね。もちろん、デバッガを使っている環境であれば、クラッシュしたときのオブジェクトとキーがわかりますので一つずつ修正していくことは可能です。

しかし今回は、クラッシュさせることなく未定義キーが渡されたことを検出する方法をご紹介します。

未定義キーが渡されたらどうなるの?

まず始めに、setValue:forKey:に未定義キーを渡すと何が起こるのか。答えは簡単、レシーバーの

- (void)setValue:(id)value forUndefinedKey:(NSString *)key

にメッセージが送信されます。

そして、このメソッドのデフォルト実装が、NSUndefinedKeyExceptionを引き起こすようになっているため、未定義キーを渡すとクラッシュするのです。

setValue:forUndefinedKey:をオーバーライドしよう

ここまで来たらあとは簡単ですね。問題を検出したいクラスで、setValue:forUndefinedKey:をオーバーライドすればよいのです。例えばこんな感じ。

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
#ifdef DEBUG
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:@"[%@] Undefined Key", NSStringFromClass([self class])]
                                                    message:[NSString stringWithFormat:@"key:%@ value:%@", key, value]
                                                   delegate:nil
                                          cancelButtonTitle:@"OK" 
                                          otherButtonTitles:nil];
    [alert show];
    [alert release];
#else
    [super setValue:value forUndefinedKey:key];
#endif    
}

レシーバーのクラス名とkey, valueをAlertViewに表示しているだけですが、意外とこれが便利だったりします。

どんな場面で便利なの?

さて、今回ご紹介したデバッグ方法ですが、常にデバッガと共にデバッグをしている方にはあまりピンとこないかもしれません。そもそも、デバッガがすべての情報を教えてくれるんだからいいじゃないの、と思うかもしれません。

でも、KVCはコード内の記述だけに使われるわけではないのです。外部データを読み込んでオブジェクトに取り込むことを考えてみましょう。そのデータは、インターネットから取ってくるかもしれないし、あらかじめ作成してバンドルに組み込んでおくかもしれません。いずれにせよ、コードとは別のところに、オブジェクトを初期化させるための情報が存在しているということになります。

このような外部データ自身をデバッグしようとしたとき、必ずしも、データ作成者がデバッガを利用しているとは限りませんよね。何よりも、ちょっとしたタイプミスを検出するために、わざわざXcodeを起動するのも手間です。

そんなとき、上記のようにアラートを利用して検出した未定義キー(タイプミスなどによるものが多い)を表示しておけば、データ作成時の手助けになるのではないでしょうか(私は今回、このような用途で使っています)。

ただし、これはあくまでもデバッグ用のメッセージ。正式リリースでの挙動をどうするかは、アプリの性質によって慎重に決める必要があります。上記の例では、DEBUGの定義がない場合は(つまり、Releaseビルドでは)通常通りクラッシュするようになっています。もちろん、リリース時にはクラッシュはなくなっている前提です。

参照ドキュメント