4

これは、ivar の代わりにプロパティを解放しているからでない限り、わかりません。誰かが問題に光を当てることができますか?

    self.dataToBeLoaded = [[NSMutableData alloc] initWithLength:10000];
    [self.dataToBeLoaded release];

警告はIncorrect decrement of the reference count of an object that is not owned by the callerです。

プロパティには、そのdataToBeLoadedセッターに関連付けられた保持属性があります。

私の理解では、alloc init は保持カウントをインクリメントし、プロパティの割り当ては保持カウントをインクリメントします。一度しか持たないので、配属後すぐに解放する。

更新 -- いくつかの実験結果:

以下のコメントで、retain プロパティが合成されたセッターに何をするかについて矛盾したアドバイスを受け取ったことを指摘したので、上記のコードをいくつかのロギングで変更して、少し実験してみようと思いました。

NSLog(@"retain 1 = %d", [dataToBeLoaded_ retainCount]);
self.dataToBeLoaded = [[NSMutableData alloc] initWithLength:10000];
NSLog(@"retain 2 = %d", [dataToBeLoaded_ retainCount]);
[self.dataToBeLoaded release];
NSLog(@"retain 3 = %d", [dataToBeLoaded_ retainCount]);

各ログ ステートメントでの結果は、0、2、および 1 でした。

どうやら、alloc または init コードにステップ インして保持カウントが 0 から 1、2 に変化するのを確認することはできないようです。NSMutableData クラスをサブクラス化することもできましたが、時間が足りませんでした。

保持カウント プロパティの値に依存することはできないとよく言われますが、私が得た情報は一貫しているように見え、例に示すようなコードの短い範囲で妥当な動作を期待できます。したがって、以前のアドバイスが正しいと信じる傾向があります。retain プロパティは、セッター内に保持を含めるという約束です。したがって、ここでは、alloc/init からの保持と、setter への呼び出しからの保持があります。したがって、保持カウントは 2 に設定されます。

このコードを実行すると:

NSMutableData *theData;
NSLog(@"retain 1 = %d", [theData retainCount]);
theData= [[NSMutableData alloc] initWithLength:10000];
NSLog(@"retain 1a = %d", [theData retainCount]);
self.dataToBeLoaded = theData;
NSLog(@"retain 2 = %d", [theData retainCount]);
[self.dataToBeLoaded release];
NSLog(@"retain 3 = %d", [theData retainCount]);

各ログ ステートメントでの保持カウントは、0、1、2、1 です。

したがって、セッターがretain. これは実際に起こっているため、ヒントというよりも約束のように見えます。

私は他の説明を受け入れます。私はこれについて傲慢になりたくありません。何が起こっているのかを正しく理解したいだけです。(この質問の件名の)警告は本当に偽りであり、心配する必要はないようです。

もう 1 つの実験は、@property ステートメントの属性としてassignではなく、使用して行われます。retain同じコードで:

NSMutableData *theData;
NSLog(@"retain 1 = %d", [theData retainCount]);
theData= [[NSMutableData alloc] initWithLength:10000];
NSLog(@"retain 1a = %d", [theData retainCount]);
self.dataToBeLoaded = theData;
NSLog(@"retain 2 = %d", [theData retainCount]);
[self.dataToBeLoaded release];
NSLog(@"retain 3 = %d", [theData retainCount]);

各ログの保持カウントは 0、1、1 (setter が保持しなかった) であり、エラー メッセージ: message sent to deallocated instance. 前回のリリースでは保持カウントがゼロに設定されていたため、割り当て解除がトリガーされました。

更新 2

最後の更新 -- 合成されたセッターが独自のコードでオーバーライドされると、セッターに明示的に含めない限り、retain 属性は監視されなくなります。どうやら (これは、ここの他のスレッドで私が言われたことと矛盾しています)、それが必要な場合は、セッターに独自の保持を含める必要があります。ここではテストしませんでしたが、おそらく最初に古いインスタンスをリリースする必要があります。そうしないと、リークされてしまいます。

このカスタム セッターには、 @propety 宣言のプロパティ属性がなくなりました。

- (void) setDataToBeLoaded:(NSMutableData *)dataToBeLoaded {
    dataToBeLoaded_ = dataToBeLoaded;
}

意味あり。合成されたセッターをオーバーライドすると、宣言されたすべてのプロパティがオーバーライドされます。合成されたセッターを使用すると、宣言されたプロパティが合成された実装で観察されます。

@property 属性は、合成されたセッターの実装方法に関する「約束」を表します。カスタム セッターを作成したら、あとは独力です。

4

4 に答える 4

4

重要なのは、以下のコードが何をしているのかを考えることです。わかりやすくするために、完全に書きます。

[self setDataToBeLoaded:[[NSMutableData alloc] initWithLength:10000]];

これにより、保持カウントが +1 のオブジェクトが作成され、 に渡されsetDataToBeLoaded:ます。(*) 次に、そのオブジェクトへの参照を破棄し、リークします。

[[self dataToBeLoaded] release];

これはdataToBeLoaded、返されたオブジェクトを呼び出して解放します。dataToBeLoadedによって返されるオブジェクトが に渡されるオブジェクトと同じであるという保証はまったくありませんsetDataToBeLoaded:。おそらくそれらは同じだと思いますし、コードを見て、常にそのように機能すると確信できるでしょうが、それは API の約束ではありません。

Antwan が投稿したコードは正しいです。

NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.dataToBeLoaded = data;
[data release];

これにより、保持カウントが +1 のオブジェクトが作成されます。次に、それをメソッドに渡し、解放します。

または、autorelease プールを使用する場合は、次のように単純化できます。

self.dataToBeLoaded = [NSMutableData dataWithLength:1000];

(*) 技術的には、selfこれにより、このメソッドが呼び出される場合と呼び出されない場合があるが、問題を混乱させるメッセージが渡されます。ほとんどの場合、メソッド呼び出しであると想定してください。ただし、プロパティを設定するだけのふりをしないでください。実際に何らかのメソッドを呼び出します。


編集:

おそらく、このコードは問題を少し明確にするでしょう。これは、一般的なキャッシング ソリューションを示しています。

.h
@interface MYObject : NSObject 
@property (nonatomic, readwrite, strong) NSString *stuff;
@end

.m
@interface MYObject ()
@property (nonatomic, readwrite, weak) MYStuffManager *manager;

@implementation MYObject

... Initialize manager ...

- (NSString*)stuff {
  return [self.manager stuffForObject:self];
}

- (void)setStuff:(NSString *)stuff {
   [self.manager setStuff:stuff forObject:self];
}

現在manager、バックグラウンドで愚かなことをしている可能性があります。のさまざまなコピーをキャッシュしている可能性がありstuffます。多分それはそれらをコピーします。たぶん、それらを他のオブジェクトにラップします。-stuff重要なのは、に渡した同じオブジェクトを常に返すことに頼ることはできないということです-setStuff:。だから絶対に手放してはいけません。

ヘッダーにはこれを示すものは何もないことに注意してください。それは発信者のビジネスではありません。しかし、呼び出し元が の結果を解放すると-stuff、デバッグが困難なクラッシュが発生します。

@synthesizeは、面倒なコード (ivar をstuff読み書きsetStuff:するコード) を記述するための省略形です。@synthesizeしかし、プロパティに使用する必要があるとは言いません。

于 2012-05-13T00:24:27.300 に答える
2

私の推測では、その方法は

- (NSMutableData *)dataToBeLoaded;

メモリ管理キーワードが含まれていないため、返されたデータを所有してないと見なされ、解放しないでください。

どちらかを使用

NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.dataToBeLoaded = data;
[data release]; data = nil;

または、実際に必要なときに遅延ロードしないのはなぜですか?

- (NSMutableData *)dataToBeLoaded;
{
    if (!_dataToBeLoaded) {
        _dataToBeLoaded = [[NSMutableData alloc] initWithLength:1000];
    }
    return _dataToBeLoaded;
}
于 2012-05-12T23:37:45.623 に答える
1

所有していないオブジェクトを解放していることを意味します。

ゲッターを使用する代わりに、インスタンス var で直接呼び出すと思いますが、分析の警告が修正されるかどうかはわかりません。また、[NSMutableData dataWithLength:1000]; を使用しないのはなぜですか。これは自動解放されるため、余分な解放呼び出しの必要がなくなります (そして、おそらくその警告も取り除かれます!)

あなたがそれを修正できる他の方法:

NSMutableData *data = [[NSMutableData alloc] initWithLength:1000];
self.databToBeLoaded = data;
[data release];
于 2012-05-12T23:29:51.763 に答える
0

ここで何が起こっているのかに答えると思われるいくつかの更新を提供しました。いくつかのテスト結果から、私の結論は、この警告は偽物であり、不適切なコードを実際に識別していないことを意味します。アップデートはそれ自体で語るべきです。それらは上に与えられています。

于 2012-05-17T06:22:14.677 に答える