1

私は次のコードを書きました:

NSString *string = [[NSString alloc] initWithFormat:@"test"];
[string release];

NSLog(@"string lenght = %d", [string length]);
//Why I don't get EXC_BAD_ACCESS at this point?

すべきだ、解放すべきだ。最後のリリース後、retainCount は 0 になるはずですが、そうでないのはなぜですか?

PS私は最新のXCodeを使用しています。

アップデート:

NSString *string = [[NSString alloc] initWithFormat:@"test"];

NSLog(@"retainCount before = %d", [string retainCount]);// => 1

[string release];

NSLog(@"retainCount after = %d", [string retainCount]);// => 1 Why!?
4

4 に答える 4

4

この場合、フレームワークは@"test"からリテラルを返す可能性がありNSString *string = [[NSString alloc] initWithFormat:@"test"];ます。つまり、リテラルが再利用される可能性があると判断し、このコンテキストで再利用します。結局、入力は出力と一致します。

ただし、プログラム内でこれらの内部最適化に依存するべきではありません。参照カウント規則と明確に定義された動作に固執してください。

アップデート

デビッドのコメントにより、私はこれを調べました。私がテストしたシステムでは、新しいオブジェクトをNSString *string = [[NSString alloc] initWithFormat:@"test"];返します。あなたのプログラムは、解放されるべきであり、不滅の文字列ステータスの資格がないオブジェクトをメッセージで送信します。

あなたのプログラムはまだ定義されていない領域に陥り、実装の詳細のアーティファクトとしてのみ、または純粋に偶然の一致として、場合によっては正しい結果が得られるように見えることがあります。David が指摘したように、リリースとログの間に「もの」を追加すると、string実際に破棄され、再利用される可能性があります。なぜこれがすべて機能するのかを本当に知りたい場合は、objc ランタイム ソースを読むか、実行時にランタイムのアセンブリをクロールすることができます。一部には説明 (ランタイム実装の詳細) が含まれている場合があり、一部はまったくの偶然です。

于 2012-06-15T19:46:30.800 に答える
3

解放されたオブジェクトに対して何かを行うことは、未定義の動作です。意味 - 時にはそれでうまくいくこともあれば、クラッシュすることもあれば、完全に別の場所で 1 分後にクラッシュすることもあれば、10 ファイル離れた変数が不思議なことに変更されることもあります。

これらの問題をキャッチするには、NSZombie 手法を使用します。調べる。それと、いくつかのコーディング規律。

今回は、解放されたメモリがまだ何も上書きされていないため、あなたは逃げました。を指すメモリにstringは、正しい長さの文字列オブジェクトのバイトがまだ含まれています。しばらくすると、別のものが存在するか、メモリ アドレスが無効になります。そして、これがいつ起こるかはわかりません。

nilただし、オブジェクトへのメッセージの送信は正当です。実際、これは Objective C で定義された動作です。何も起こらず、0 または nil が返されます。

于 2012-06-15T19:46:22.813 に答える
3

アップデート:

Ok。私は疲れていて、あなたの質問を十分に注意深く読んでいませんでした。

あなたがクラッシュしない理由は純粋な運です。最初は、initWithString:文字列リテラルに関するすべての回答 (私の元の回答 (以下) を含む) が有効になる場合に使用していると思いました。


「純粋な運」とは

これが機能する理由は、オブジェクトは解放されますが、ポインターは以前の場所を指し続け、再度読み取る前にメモリが上書きされないためです。したがって、変数にアクセスすると、そのままのメモリから読み取られます。つまり、有効なオブジェクトが返されます。上記のことを行うことは非常危険であり、最終的には将来クラッシュする可能性があります!

リリースとログの間にさらにオブジェクトを作成し始めると、そのうちの 1 つが文字列と同じメモリを使用し、古いメモリを読み取ろうとするとクラッシュする可能性があります。

log を 2 回続けて呼び出すとクラッシュするほど脆弱です。


元の答え:

文字列リテラルは解放されません!

これがなぜなのかについては、この質問に対する私の回答をご覧ください。

この回答にも良い説明があります。

于 2012-06-15T19:55:31.387 に答える
2

考えられる説明の 1 つ: 定数を使用するだけでなく、余分に動的に文字列を割り当てています。おそらくCocoaは、それが単なるメモリの浪費であることをすでに知っているため(可変文字列を作成していない場合)、割り当てられたオブジェクトを解放し、代わりに定数文字列を返す可能性があります。また、一定の文字列では、リリースと保持は効果がありません。

これを証明するには、返されたポインターを定数文字列自体と比較する価値があります。

int main()
{
    NSString *s = @"Hello World!";
    NSString *t = [[NSString alloc] initWithFormat:s];

    if (s == t)
        NSLog(@"Strings are the same");
    else
        NSLog(@"Not the same; another instance was allocated");

    return 0;
}
于 2012-06-15T19:46:13.360 に答える