2

メソッドが呼び出されたSample後に、このコードがクラスのインスタンスをリークする (過度に保持する) 理由について何か考えはありますか? [startSampling:action:]プロファイラーは、サンプリングが完了した後に正の保持カウントを示します (つまり、sample()ブロックは を返しますYES)。ARCは明らかに有効になっています。

@implementation Sample

- (void)startSampling:(BOOL (^)(Sample *sender))sample action:(void (^)(Sample *sender))action {
    __block void (^next)(Sample *sender) = nil;

    void (^block)(Sample *sender) = ^(Sample *sender) {
        if (sample(sender)) {
            action(sender);
        } else {
            [self performBlock:next afterDelay:self.duration / 100.0];
        }
    };

    next = block;

    [self performBlock:block afterDelay:self.duration / 100.0];
}

@end
4

2 に答える 2

4

そのメソッドでブロックを作成しています。ブロックは基本的にstruct、ブロックで使用される外部定義変数のそれぞれのフィールドに加えて、ブロックに対して実行されるコードへのポインターなどの追加のものがあります。

struct TheBlock {
    void (*function)(TheBlock *);
    // other bookkeeping fields

    __strong TheBlock *next;
    __strong OtherBlockType *sample;
    __strong OtherBlockType *action;
    __strong Sample *self;    
};

すると、そのフィールドがそれを含む構造体を指すようにnext = block;設定されます。nextしたがって、ブロックはそれ自体を保持します。これは保持サイクルであり、ブロックが解放されるのを防ぎます。また、ブロックも保持され、インスタンスが解放されるselfのを防ぎます。Sample

それを修正する 1 つの方法は、作業が完了したときに次のように設定nextすることです。nil

void (^block)(Sample *sender) = ^(Sample *sender) {
    if (sample(sender)) {
        action(sender);
        next = nil;
    } else {
        [self performBlock:next afterDelay:self.duration / 100.0];
    }
};

これにより、ブロックが不要になったときに保持サイクルが中断され、ブロックとSampleインスタンスの割り当てが解除されます。

于 2013-11-14T17:46:45.593 に答える
2

変数nextはブロックによってキャプチャされます。ブロックは、コピー時にオブジェクト ポインター型のキャプチャされた変数を保持します (実際には、ブロック ポインター型の変数であるため、保持される代わりにコピーされます)。ARC では、__block変数も保持されます。nextブロックを指すように設定されているため、ブロックはそれ自体への強い参照を持っています。そのため、保持サイクルがあります。

これを修正するにはnext、弱い参照を作成するだけです。

__block __weak void (^next)(Sample *sender) = nil;
于 2013-11-15T09:10:20.743 に答える