5

localComplete ブロックと self.block のメモリ アドレスが同じである理由を教えてください。self.complete のプロパティは copy に設定されています。念のため、self.complete に割り当てるときに localComplete で copy も呼び出します。

- (void) test {

    CompletionBlock localComplete = ^{

    };

    NSLog(@"localComplete - %p", localComplete);

    self.block = [localComplete copy];

    NSLog(@"self.complete - %p", self.block);

    self.block();
}

出力は次のとおりです。

2013-10-05 08:39:18.549 TestApp[90703:a0b] localComplete - 0x60b8
2013-10-05 08:39:18.550 TestApp[90703:a0b] self.complete - 0x60b8

別の例として、文字列を作成します。

// creating string
self.carType = [[NSString alloc] initWithFormat: @"Good%@", @"year"];
NSLog(@"self.carType - %p", self.carType);

// same memory address???
NSString *carInitString = [[NSString alloc] initWithString: self.carType];
NSLog(@"carInitString - %p", carInitString);

// same memory address???
NSString *carCopy = [self.carType copy];
NSLog(@"carCopy - %p", carCopy);

// different memory address
NSString *carInitWithFormat = [[NSString alloc] initWithFormat: @"%@", self.carType];
NSLog(@"carInitWithFormat - %p", carInitWithFormat);

そして出力:

2013-10-05 09:45:01.667 TestApp[91103:a0b] self.carType - 0xa084910
2013-10-05 09:45:01.668 TestApp[91103:a0b] carInitString - 0xa084910
2013-10-05 09:45:01.668 TestApp[91103:a0b] carCopy - 0xa084910
2013-10-05 09:45:01.668 TestApp[91103:a0b] carInitWithFormat - 0xa336b70

carInitString と carCopy のメモリ アドレスが異なるのはなぜですか? プロジェクトのビルド設定で最適化がオフになっています。

4

2 に答える 2

3

元の質問に関して、ブロックは通常スタックに割り当てられます。Block_Copy(関数またはメソッドのいずれかを使用して) ブロックをコピーすると-copy、スタック上のブロックが移動します (さらに呼び出すと、ブロックの保持カウントが増加するだけです)。

Block_copy [...]、指定されたブロック ポインターは、基になるブロック オブジェクトをヒープにコピーし、その参照カウントを 1 に設定して新しいブロック ポインターを返すか、または (ブロック オブジェクトが既にヒープ上にある場合) その参照を増やします。 1ずつ数えます

ソース

したがって、あなたの例では、最初のブロックはローカルであるのに対し、2 番目のブロックはヒープ上にあるため、異なるアドレスが予想される場合がありますが、その特定のブロックは周囲のスコープへの参照を行わないため、コンパイラはそれをグローバルブロック。グローバル ブロックはスタックに割り当てられず、代わりにメモリ内の固定位置にあります。

グローバル ブロックをコピーしても、ブロックはどこにも移動しません。オブジェクトはすでにヒープ上にあるため、保持カウントが増えるだけです。そのため、2 つの異なるアドレスを取得することはありません。

周囲のコンテキストへの参照を使用してブロックを作成しようとすると、2 つの異なるアドレス (スタックとヒープ) が作成されます。


では、 についての質問に答えましょうNSString

不変オブジェクトをコピーすると、クラスがプロトコルをretain実装する方法に応じて、最適化として a が発生する可能性があります (つまり、フレームワーク設計の最適化であり、コンパイラはそれとは関係ありません) 。NSCopying

NSStringこれは、 、NSArrayNSSet...などの多くの Foundation クラスに当てはまります。

ドキュメントNSCopyingで読むことができるように、これはプロトコル仕様に完全に準拠しています:

このプロトコルを実装するためのオプションは次のとおりです。

...

  • クラスとそのコンテンツが不変の場合、新しいコピーを作成する代わりに元のコピーを保持することで、NSCopying を実装します。

コメントで Greg Parker が指摘した-[NSString initWithString:]ように、同じ種類の最適化を実行します。不変の文字列を引数として渡すと、同じ文字列が保持されて返されます。


これは、いくつかの状況で役立つ動作です。例を次に示します: プロパティを宣言します。

@property (nonatomic, copy) NSArray *anArray;

インターフェイスで公開します。

copy作業中のオブジェクトが後でクライアントによって変更されないようにするために、それを as として宣言することをお勧めします。あなたがそれを保持し、クライアントが渡したNSMutableArray場合、彼女がオブジェクトを操作するのを防ぐことはできません.

したがって、コピーは良いことですが、それには代償が伴うようです。必要のないときでもオブジェクトをコピーすることになります (つまり、不変です)。

ただし、上記の動作のおかげで、そのような代償を払う必要はありません。に送信copyするNSArrayと保持されますが、送信するNSMutableArrayと実際にはコピーされるため、この場合は大きな勝利です。

于 2013-10-05T17:01:05.060 に答える