NSErrorは初期化してから使いましょう(なにをいまさら)
ライブラリのテストケースを書いているのですが、あるテストケースが必ずエラーになるので悩んでいたのですが昨日ようやく判明しました。ケースは渡したNSErrorがnilであること(テスト対象メソッドがエラーを戻さないこと)をテストしているんですが、なんのことはないNSErrorを初期化していなかったためでした。
どこで読んだのか(あるいは勘違いなのか)"Objective-Cでは変数は宣言した時点でnilで初期化される"みたいな思い込みがあって全然気がつきませんでした。気になってアップルのError Handling Programming Guide For Cocoaを読んでみたら、
NSError *theError;
BOOL success = [myDoc writeToURL:[self docURL]
ofType:@”html”
error:&theError];
if (success == NO) {
// maybe try to determine cause of error and recover first
NSAlert *theAlert = [NSAlert alertWithError:theError];
[theAlert runModal]; // ignore return value
}というサンプルや
NSError *theError;
NSData *theData = [doc dataOfType:@”xml” error:&theError];
if (!theData && theError)
[anyView presentError:theError
modalForWindow:[doc windowForSheet]
delegate:self
didPresentSelector:
@selector(didPresentErrorWithRecovery:contextInfo:)
contextInfo:nil];
など、必ずしもtheErrorを初期化していません。初期化しない場合上記コードのように戻り値を同時に判定する処理が必要になりますね。直接theErrorを判定するようなコードの場合はやっぱりnilで初期化されていました。
NSError* theError = nil;
[obj:&error];
if (theError)
{
:
}僕は、C/C++で変数を宣言する場合だったら
char aString[256];
memset(aString,0x00,sizeof(aString));と、不要に思えても初期化するタイプなんですが(これに異論がある人もいるようですね)、Objective-Cでは大抵の場合、宣言と生成が同時になされるような書き方をすることが断然多いからか(言い訳くさい)こういうことは不要だと思い込んでいました。それで今回の場合のように、戻り値を確認しないでいきなり
NSError *theError;
NSData *theData = [doc dataOfType:@”xml” error:&theError];
STAssertNil(theError,@"must nil");みたいなケースを書いてしまったものだから迷う羽目になったわけです。ということはですよ、これまで書いたコードもこういう実装をしている可能性大ってことですよねぇ。あぁ、恐ろしい(あるいはエラーなんてロクにチェックしていないとか)。









Comments
こんにちは。僕が仕事を始めた20年くらい前は初期化するのは当然だったような気がします。当時は、デバッグでメモリダンプなんかを読んだりすることもあったため初期化しておかないとダンプが見づらく、必ず初期化するようにという先輩のアドバイスもすんなり受け入れていました。
まあ、今はデバッガのもGUIが当たり前だしこういうことに異を唱える人の考えも理解できないことはないですが「三つ子の魂百まで」ってやつで。(^^)
おっしゃるとおりですね。細部にまで丁寧に気を遣ったコードが美しいと思います。大変勉強になります。良きアドバイスありがとうございます。
C/C++での初期化は私も賛成です。
> char aString[256];
> memset(aString,0x00,sizeof(aString));
これは"char aString[256]={0};"の方が関数呼び出しなどのオーバーヘッドも無くなって良いと思います。
最適化オプション次第では同じコードを吐くのかもしれませんが、ご参考まで。
(組み込み業務をしているせいで細かい指摘になってしまいスミマセン)