为什么这段代码会泄漏Sample类的实例?

时间:2013-11-14 17:31:26

标签: objective-c memory-leaks objective-c-blocks

有关为什么此代码在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

2 个答案:

答案 0 :(得分: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实例被释放。

解决此问题的一种方法是在完成后将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实例被释放。

答案 1 :(得分:2)

块捕获变量next。块在复制时保留任何捕获的对象指针类型的变量(实际上,因为它是块指针类型的变量,所以它被复制而不是保留)。在ARC下,也会保留__block个变量。 next设置为指向块,因此块具有对其自身的强引用。这就是你有一个保留周期的原因。

要解决此问题,您只需将next作为弱引用:

__block __weak void (^next)(Sample *sender) = nil;