2009-07-02 17 views
20

私はiPhoneアプリケーションで作業しています。クラスのオブジェクトがあり、クラスBlockの多数のオブジェクトをリリースする必要があります。 Blockには、現在、クラスのインスタンス変数を保持するプロパティがあります。私は循環参照を持っています。 Objective-Cで弱い参照を作成するにはどうすればよいですか?

@interface Block : UIImageView { 
    Row *yCoord; 
} 
@property (nonatomic,retain) Row *yCoord; 
@end 

すべては、これらのブロックのNSMutableArrayが含まれています。

@interface Row : NSObject { 
    NSMutableArray *blocks; 
} 
-(void)addBlock:(Block*)aBlock; 
@end 

@implementation Row 
-(void)addBlock:(Block*)aBlock { 
    [blocks addObject:aBlock]; 
    aBlock.yCoord = self; 
} 
@end 

これは循環参照であることを理解しています。 Appleのマニュアルには、循環参照を持つオブジェクトの割り当てを解除するには、強い参照(保持プロパティ)の代わりに弱い参照が必要だと言われています。私は、Row内のすべてのBlocksとRo​​w自身を同時に解放し、解除する予定です。私のブロック内の弱い参照を "親"行に設定するにはどうすればよいですか?

+4

ちなみに、編集や適切なタグ付け、特に新しいSOユーザーとしてのご褒美をお寄せいただければ幸いです。ブラボー!私は、コミュニティの利益のために物事を少し上手にする時間を取る人々に感謝します。 –

+2

なぜ、ありがとう!このサイトはまさに私があらゆる面で探していたものです。非常に多くの人々がすぐに飛び出してそれに答える意思があるときに、明確な質問をする時間を取ることが理にかなっています。 – Tozar

答えて

19

編集:編集者:askerは彼がガベージコレクションを使用していないことを明確にしているので(iPhoneは現在サポートしていません)、私の助言は、オブジェクトの1つだけを残してサイクルを避けることです。デリゲート。プロパティを使用する場合は、これを達成するために「保持」の代わりに「割り当て」を使用します。例:

@property (nonatomic,assign) Row *yCoord; 

残りの回答は、Objective-C 2.0とGCに関して「弱い参照」に関連しています。


あなたがガベージコレクション(X 10.5+)で作業している、弱い参照は__weakで変数宣言を付けることによって作成されます。その変数に代入すると、GC(参照されている場合)は参照を追跡し、参照されたオブジェクトへの強い参照がすべて消えると、自動的にその参照をゼロにします。

したがって、次のようにガベージコレクション(現在は10.5 +以上、おそらくいつか)でより適切に再生することができます: related Apple docs

@property (nonatomic,assign) __weak Row *yCoord; 

あなたは)もっと詳細な情報を見つけることができChris Hanson(引用する:。

を「__weakとインスタンス変数の宣言を付けることによって、あなたはそれだけだ場合、そのガベージコレクタを伝えますrそのオブジェクトが収集可能であると考えられるべきであるオブジェクトへの反論」

「オブジェクトへの弱い参照がない場合」と言うことで明確になります。最後の強い参照が削除されるとすぐに、オブジェクトが収集され、すべての弱い参照が自動的にゼロにされます。

注:これは弱参照を作成するに関連直接ではありませんが、そこ__strong属性でもあるが、Objective-Cのオブジェクト変数は、デフォルトでは、強い参照であることから、それは一般的にのみ使用されますガベージコレクタがルーツとして扱わない構造体やプリミティブのようなものへの未処理のCポインタ。強いものとして宣言しなければ、あなたのもとから収集されます。 (__weakの欠如がサイクルとメモリリークを維持し、__strongの欠如はメモリ踏みおよび非決定論的に発生し、本当に奇妙な陰湿なバグにつながることができますし、追跡するのは非常に困難なことが発生する可能性があります一方。)

+1

これはiPhoneアプリケーション用であることを明確にします。私はiPhoneがGCを使用しているとは思わない。 – Tozar

+1

を宣言する代わりに、 Row * yCoord; with @property(非原子、保持)\t行* yCoord; 私は何をしますか: __weak Row * yCoord; しかし、どのようにプロパティを宣言するのですか? – Tozar

+0

@propertyと__weakを混在させることができます。@property(非構造、保持)__weak Row * yCoord; – Tim

4

弱いです参照は単なる割り当てです(あなたがワームの別々の缶であるが、保持サイクルに悩まされていないガベージコレクションについて話している場合を除きます)。

通常は、ココアでは、は(NSMutableArrayのに含めることによって)Blockオブジェクトを保持するだろうが、Blockはを保持しない、それぞれが単に(「割り当て」プロパティで)IVARに格納あろう。それが割り当て解除される前に

限りとして各Blockを解放するように注意され(すなわち、そのdealloc限り他の誰もそれらへのポインタを持っていないようブロックを解放しますNSMutableArrayの解放する必要があります)、その後、すべては次のように割り当て解除されます適切な。

あなたはまた、配列からentiriesを削除する前に、ブロックからの行の参照をゼロの予防措置をとることができ、何かのように:_blocksは、ブロックプロパティによって参照さIVARある

- (void) dealloc { 
    for (Block* b in _blocks) { 
     b.row = nil; 
    } 
    [_blocks release]; 
    [super dealloc]; 
} 

+0

_b NSMutableArray ivarはself.blocksから返されますか?もしそうなら、代わりに[_b release]を意味すると思います。また、参照を保持するクラスの中でself.blocksを使う必要はありません。(forbのBlock * b)を使うだけです。 _bが "_blocks"または "blocks"という名前であった場合、混乱は少なくなります。 –

+0

固定、ありがとうQuinn –

8

これをretainの代わりに代入するように変更するだけで、循環参照はなくなります。弱参照を作成するために割り当てる使用

@interface Block : UIImageView { 
    Row *yCoord; 
} 
@property (nonatomic,assign) Row *yCoord; 
@end 
3

いずれかのオブジェクトが第三の目的で保持し、その後、他のオブジェクトを逆参照するために使用することができる場合は特に、マルチスレッドシステムで危険であることができます。

幸いにも、これは階層の問題であることが多く、ウィークリファレンスを含むオブジェクトは参照されたオブジェクトの存続期間を参照するオブジェクトのみを気にします。これは、スーペリア< - >従属関係の通常の状況です。

私はOPのコメントの例がこれにマップされていると思います。Row = Superior、Block = Subordinateです。この場合

、私は部下から上司に参照するためにハンドルを使用します。

// Superior.h 

@class Superior; 

@interface SuperiorHandle : NSObject { 
    @private 
     Superior* superior_; 
} 

// note the deliberate avoidance of "nonatomic" 
@property (readonly) Superior *superior; 

@end 

@interface Superior : NSObject { 
    @private 
     SuperiorHandle *handle_; 
     // add one or more references to Subordinate instances 
} 

// note the deliberate avoidance of "nonatomic" 
@property (readonly) SuperiorHandle *handle; 

@end 


// Superior.m 

#import "Superior.h" 

@implementation SuperiorHandle 

@synthesize 
    superior = superior_; 

- (id)initWithSuperior:(Superior *)superior { 
    if ((self = [super init])) { 
     superior_ = superior; // weak reference 
    } 
} 

- (void)invalidate { 
    @synchronized (self) { 
     superior_ = nil; 
    } 
} 

- (Superior *)superior { 
    @synchronized (self) { 
     // retain and autorelease is required to prevent dealloc before we're ready, thanks to AndroidDev for pointing out this mistake 
     return [[superior_ retain] autorelease]; 
    } 
} 

@end 

@implementation Superior 

@synthesize 
    handle = handle_; 

- (id)init { 
    if ((self = [super init])) { 
     handle_ = [[SuperiorHandle alloc] initWithSuperior:self]; 
    } 
    return self; 
} 

- (void)dealloc { 
    [handle_ invalidate]; 
    [handle_ release]; 

    [super dealloc]; 
} 

@end 


// Subordinate.h 

@class Superior; 
@class SuperiorHandle; 

@interface Subordinate : NSObject { 
    @private 
     SuperiorHandle *superior_handle_; 
} 

@property (readonly) Superior *superior; 

@end 


// Subordinate.m 

#import "Subordinate.h" 

#import "Superior.h" 

@implementation Subordinate 

// no synthesize this time, superior's implementation is special 

- (id)initWithSuperior:(Superior *)superior { 
    if ((self = [super init])) { 
     superior_handle_ = [superior.handle retain]; 
    } 
    return self; 
} 

- (void)dealloc { 
    [superior_handle_ release]; 

    [super dealloc]; 
} 

- (Superior *)superior { 
    @synchronized (superior_handle_) { 
     return superior_handle_.superior; 
    } 
} 

@end 

いくつかの利点:

  1. それは、スレッドセーフです。下位レベルに含まれる弱参照を無効なポインタにすることはできません。それは無限になるかもしれませんが、それは問題ありません。
  2. オブジェクト自体には、埋め込まれた弱参照について知る必要があります。他のすべてのオブジェクトは、スーペリアに対する通常の参照を持つように従属オブジェクトを扱うことができます。
+0

このデザインはうまくいくが、SuperiorHandleクラスで@synthesize superiorを使うことはできない。 @synthesizeは、フィールドごとに独立したロックを作成します(スピンロックは正確です)。 'superior_'変数を返すときに行う '無効化'で使用するのと同じロックをロックする必要があります。だからあなたは自分自身を実装して@synchronized(self){returnValue = _superior}を返す必要がありますreturn returnValue; –

+0

実際、この「スーペリア」デザインはうまくいかないかもしれません。 '上級'の値を取得するために '上位'メソッドを呼び出し、別のスレッドがハンドルを無効にするとどうなりますか?あなたはまだ、「優位」な値を取得したスレッドで解放されたメモリへのポインタが残っています。もちろん、これはスレッドセーフではないと仮定していない限りです(ただし、 @synchronize呼び出しが必要です)。残念ながら、私はこれがまったく機能しないと思います。これを動作させるには本当にGC環境が必要です。返す前に優位な値を保持または自動解放する必要があります。 –

+0

Re:スピンロック。私はこの動作を認識していませんでしたが、そのドキュメントがありますか?私がHillegassの本で読んだことと、文書化されているもの(ここではhttp://stackoverflow.com/questions/588866/atomic-vs-nonatomic-properties)は、「アトミック合成」によって生成されたロックが@私は手動でinvalidate()を行うのと同じように(self)同期します。 – Brane

関連する問題