2

关于为什么在调用方法Sample后此代码会泄漏(过度保留)类的实例的任何想法?[startSampling:action:]Profiler 在采样完成后显示正保留计数(即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字段设置为指向包含它的结构。所以block会保留自己,这是一个retain循环,阻止block被释放。并且块也保留self,防止Sample实例被释放。

修复它的一种方法是设置nextnil完成它的时间:

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 回答