0

ファイルから文字列を読み取る次の 2 つの方法を検討してください。

NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:NULL];

NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
NSFileHandle *file = [NSFileHandle fileHandleForReadingAtPath:path];
NSData *data = [file readDataToEndOfFile];
NSString *string = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
[file closeFile];

私は方法 1 に頼ることを好みますが、次のコンテキストで使用すると奇妙な動作をします。

NSString *string; // CLASS VARIABLE
(void) setupView
{
  string = ...; // LOADING THE STRING
}
(void) drawView
{
 ...;  // USING THE STRING
}

要するに、これは NSTimer に基づく OpenGL-ES 描画ループです。問題は、最初のフレームでのみ文字列にアクセスできることです。次のフレームで、文字列にアクセスしようとすると、iPhone シミュレーター (2.2) がクラッシュします。

おそらく「私のコード、または使用している OpenGL-ES コードの何か」と言う人がいるでしょう...しかし、方法 2 を使用して文字列をロードすると、すべてが意図したとおりに機能するという奇妙な事実をどのように説明すればよいでしょうか?

手がかりはありますか?

4

2 に答える 2

4

allocCocoaではnew、、、、copyまたはを使用してオブジェクトを作成した場合にのみ、オブジェクトを所有しますretain。オブジェクトを所有していない場合は常に、それを使用するローカルスコープ外のオブジェクトについて保証することはできません。あなたの例では、上記のいずれの方法でも新しい文字列を作成しなかったため、後で保存した場合にその文字列が存在するという保証はありません。

まだ所有していない(つまり、前述のメソッドの1つを使用して作成しなかった)オブジェクトの所有権を取得するため(つまり、後で使用できるようにオブジェクトを保持するため) 、そのオブジェクトに保持メッセージを送信する必要があります。たとえば、次のようにコードを書き直すと、文字列を所有することになり、後で使用することを心配する必要がなくなります。

// option 1
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
NSString *string = [[NSString alloc] initWithContentsOfFile:path encoding:NSASCIIStringEncoding error:NULL];

// option 2
NSString *path = [[NSBundle mainBundle] pathForResource:@"foo" ofType:@"txt"];
NSString *string = [NSString stringWithContentsOfFile:path encoding:NSASCIIStringEncoding error:NULL];
[string retain];

allocオプション1は、メッセージを使用して文字列を具体的に作成するため、問題ありません。これで、別のときに所有権を放棄するまで、文字列オブジェクトを所有できます。特定の状況を考えると、これはおそらく最良のオプションです(つまり、文字列オブジェクトを後で使用するために、コンビニエンスコンストラクターを実際に使用したくない場合)。

オプション2は、文字列を送信することで文字列の所有権を明確に取得するため、問題ありませんretainalloc/initあなたの場合、すでに利用可能なオプションがあり、通常は読みやすいため、これはおそらく最良のオプションではありません 。

オブジェクトの処理が完了し、オブジェクトの所有権を放棄したい場合は、そのオブジェクトにreleaseメッセージを送信します。releaseCocoaでは、オブジェクトにメッセージを送信することは、オブジェクトを破棄することを意味するのではなく、所有権を放棄することを意味することを覚えておくことが重要です。

Cocoaのメモリ管理は、オブジェクトの所有権という考え方に基づいて設計されています。最初は少し混乱するかもしれませんが、頭を包むと、メモリエラー、リーク、その他のバグを発生させることなく、環境でのプログラミングがはるかに簡単になります。コンビニエンスメソッド(のようなstringWithString)は、実際には短時間だけ、単一の関数またはプログラムブロックの範囲内で使用するオブジェクトを作成する場合に使用するように設計されています。オブジェクトをそのスコープを超えて保持することを計画している場合は、alloc/initまたはnewメソッドを使用してオブジェクトを構築することをお勧めします。

詳細については、Cocoaのメモリ管理プログラミングガイドをお読みください。これは、Cocoa開発者にとって必読と考えられています。また、特に他のメモリ管理モデルの経験が豊富な場合は、通常、それを読み、実験し、さらに数回読んで実際に把握する必要があります。

于 2009-01-14T15:44:33.347 に答える
1

Class メソッドを使用して String を作成すると、通常は一番上の autorelease プールに追加されます。イベント ループの最後に、プールは保持しているすべてのオブジェクトにリリース メッセージを送信します。この場合、新しく作成された文字列の保持カウントは 1 で、ループの最後で 0 になり、割り当てが解除されます。文字列を保持したい場合は、retain メッセージを送信して、イベント ループが終了したときに保持カウントを正の値に保ちます。

于 2009-01-14T15:12:42.160 に答える