20

これにより、何らかの保持サイクルが発生しますか? 安全に使用できますか?

__block void (^myBlock)(int) = [^void (int i)
{
    if (i == 0)
        return;

    NSLog(@"%d", i);
    myBlock(i - 1);
} copy];
myBlock(10);

myBlock = nil;
4

6 に答える 6

34

コードには保持サイクルmyBlock含まれていますが、再帰の基本ケース ( ) で nil に設定することにより、再帰の終わりに保持サイクルを中断できますi == 0

これを証明する最善の方法は、「停止時に記録されていないデータを破棄する」をオフにし、「参照カウントを記録する」をオンにし、「アクティブな割り当てのみを追跡する」をオフにして、割り当てインストルメントの下で実行することです。

OS X Command-Line Tool テンプレートを使用して新しい Xcode プロジェクトを作成しました。プログラム全体は次のとおりです。

#import <Foundation/Foundation.h>

void test() {
    __block void (^myBlock)(int) = [^void (int i){
        if (i == 0) {
//            myBlock = nil;
            return;
        }
        NSLog(@"myBlock=%p %d", myBlock, i);
        myBlock(i - 1);
    } copy];
    myBlock(10);
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        test();
    }
    sleep(1);
    return 0;
}

次に、上で説明した設定を使用して、割り当てインストルメントの下で実行しました。次に、Instruments で「Statistics」を「Console」に変更して、プログラムの出力を確認しました。

2012-10-26 12:04:31.391 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 10
2012-10-26 12:04:31.395 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 9
2012-10-26 12:04:31.396 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 8
2012-10-26 12:04:31.397 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 7
2012-10-26 12:04:31.397 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 6
2012-10-26 12:04:31.398 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 5
2012-10-26 12:04:31.398 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 4
2012-10-26 12:04:31.399 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 3
2012-10-26 12:04:31.400 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 2
2012-10-26 12:04:31.401 recursiveBlockTest[71789:303] myBlock=0x7ff142c24700 1
<End of Run>

ブロックのアドレス ( 0x7ff142c24700) をコピーし、「Console」を「Objects List」に変更して、アドレスを検索ボックスに貼り付けました。楽器はブロックの割り当てだけを示しました:

ブロック漏れ

Live 列の下のドットは、プログラムが終了したときにブロックがまだ割り当てられていたことを意味します。漏れました。アドレスの横にある矢印をクリックして、ブロックの割り当ての完全な履歴を表示しました。

漏洩した詳細をブロックする

この割り当てで発生したことは 1 つだけです。それは、割り当てられたことです。

次に、ステートメントmyBlock = nil内の行のコメントを外しました。if (i == 0)次に、プロファイラーの下で再度実行しました。システムはセキュリティのためにメモリ アドレスをランダム化するため、検索バーをクリアしてから、この実行でブロックのアドレスを再度コンソールで確認しました。今回でし0x7fc7a1424700た。再び「オブジェクト リスト」ビューに切り替えて、新しいアドレスを貼り付けました0x7fc7a1424700。これが私が見たものです:

ブロック解放

今回は Live 列の下にドットがありません。これは、プログラムが終了するまでにブロックが解放されたことを意味します。次に、アドレスの横にある矢印をクリックして、完全な履歴を表示しました。

ブロック解除された詳細

今回は、ブロックが割り当てられ、解放され、解放されました。

于 2012-10-26T17:17:43.860 に答える
2

ARC を使用している場合は、__blockオブジェクト変数がブロックによって保持されるため、保持サイクルがあります。したがって、ブロックはそれ自体を保持します。myBlockと の両方__blockを宣言することで回避できます__weak

MRC を使用している場合、__blockオブジェクト変数は保持されず、問題はないはずです。最後にリリースすることを忘れないmyBlockでください。

于 2012-10-27T19:17:56.517 に答える
1

警告が表示されないソリューションが必要でした。このスレッドでhttps://stackoverflow.com/a/17235341/259521 Tammo Freese が最適なソリューションを提供します。

__block void (__weak ^blockSelf)(void);
void (^block)(void) = [^{
        // Use blockSelf here
} copy];
blockSelf = block;
    // Use block here

彼の説明は完全に理にかなっています。

于 2014-08-10T15:05:15.057 に答える
1

いいえ、保持サイクルは発生しません。__blockキーワードはブロックにコピーしないように指示します。myBlockこれは、割り当ての前に発生し、アプリケーションがクラッシュする可能性があります。これが ARC でない場合はmyBlock、 を呼び出した後にリリースするだけですmyBlock(10)

于 2012-10-26T16:35:10.683 に答える
0

この問題に対する最新の解決策は次のとおりです。

void (^myBlock)();
__block __weak typeof(myBlock) weakMyBlock;
weakMyBlock = myBlock = ^void(int i) {
    void (^strongMyBlock)() = weakMyBlock; // prevents the block being delloced after this line. If we were only using it on the first line then we could just use the weakMyBlock.
    if (i == 0)
        return;

    NSLog(@"%d", i);
    strongMyBlock(i - 1);
};
myBlock(10);
于 2016-01-14T01:56:16.877 に答える