バグを最小限の再現可能なケースに絞り込もうとしているところ、奇妙なことがわかりました。
次のコードを検討してください。
static NSString *staticString = nil;
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if (staticString == nil) {
staticString = [[NSArray arrayWithObjects:@"1", @"2", @"3", nil] componentsJoinedByString:@","];
}
[pool drain];
NSLog(@"static: %@", staticString);
return 0;
}
このコードがクラッシュすることを期待しています。代わりに、以下をログに記録します。
2011-01-18 14:41:06.311 EmptyFoundation[61419:a0f] static: static:
ただし、次のように変更するNSLog()
と:
NSLog(@"static: %s", [staticString UTF8String]);
その後、クラッシュします。
もう少し情報を編集します:
プールを排水した後:
NSLog(@"static: %@", staticString); //this logs "static: static: "
NSLog(@"static: %@", [staticString description]); //this crashes
したがって、明らかに文字列に対してメソッドを呼び出すだけで、クラッシュするのに十分です。その場合、文字列を直接ログに記録してもクラッシュしないのはなぜですか? NSLog()
メソッドを呼び出すべきではありません-description
か?
2 番目の「 static: 」はどこから来ているのですか? なぜこれがクラッシュしないのですか?
結果:
ケビン・バラードとグラハム・リーの両方が正しい. (私が誤って想定していたように) それが呼び出されてNSLog()
いないことを認識した Graham の意見は正しかったし、Kevin は、これがフォーマット文字列のコピーと周り-description
の奇妙なスタック関連の問題であることはほぼ間違いなく正しい。va_list
NSLogging
をNSString
呼び出しません-description
。Graham はこれをエレガントに示しました。ロギングを行う Core Foundation のソースをたどると、これが事実であることがわかります。内部で発生したバックトレースは、 => => =>NSLog
を呼び出していることを示しています。 (5365 行目) ですべての魔法が実行されます。すべての置換を手動で検索していることがわかります。置換の型が aであり、記述関数が非 nil であり、置換が別の型によってまだ処理されていない場合にのみ、記述コピー関数を呼び出すことになります。説明がコピーされていないことを示したので、NSLogv
_CFLogvEx
_CFStringCreateWithFormatAndArgumentsAux
_CFStringAppendFormatAndArgumentsAux
_CFStringAppendFormatAndArgumentsAux()
%
CFFormatObjectType
NSString
より早く処理されます (その場合、おそらく未加工のバイト コピーを実行することになるでしょう)。- Kevin が推測しているように、ここでスタック エラーが発生しています。どういうわけか、自動解放された文字列を指していたポインターが別のオブジェクトに置き換えられています
NSString
。だから、それはクラッシュしません。変。ただし、静的変数の型を などの別のものに変更するとNSArray
、-description
メソッドが呼び出され、プログラムは予想どおりにクラッシュします。
本当にまったく奇妙です。ポイントは、行動の根本原因について最も正確であるケビンに与えられ、グラハムに私の誤った考えを正したことを称賛します. 2つの答えを受け入れることができればいいのに...