CALayer导致ARC内存泄漏

时间:2018-09-04 11:03:57

标签: ios memory-leaks core-animation automatic-ref-counting

我花了一些时间来理解我的代码中的一些大内存泄漏,因此在简化代码之后,剩下的是:

@interface TestLayer: CALayer
@end
@implementation TestLayer
-(void)dealloc
{
    NSLog(@"dealloc called");
}
@end

@implementation AppDelegate
#define ENABLE_LEAK 1
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    for (int i=0; i<10; i++) {
        @autoreleasepool {
            TestLayer* test = [TestLayer layer];
#if ENABLE_LEAK
            CALayer* l = [CALayer layer];
            [test addSublayer:l];
            [l removeFromSuperlayer];
            l = nil;
#endif
            test = nil;
        }
    }
return YES;
}
.....

如果ENABLE_LEAK设置为0,则正确调用TestLayer中的dealloc 10次。 但是,如果将其设置为1,则在此之前不会调用TestLayer中的dealloc,它会返回应用程序didFinishLaunchingWithOptions:。 实际上只是调用[test setNeedsLayout];没有添加任何子层会导致TestLayer泄漏。

我正在使用类似的代码来生成一些脱机内容,并且不会用于仅使用预生成的脱机内容的最终应用程序。

有人知道我的TestLayer是什么,如何说服它释放它?

3 个答案:

答案 0 :(得分:2)

正如 Cristi 在其中一项评论中所建议的那样,仅使用CALayer就解决了我所有与内存泄漏CATransaction.flush()有关的问题

它就像一个魅力?

答案 1 :(得分:1)

doc for CATransaction中,如果您修改层而不显式打开一个层,则将创建一个隐式事务。该事务在下一次运行循环迭代时自动刷新。由于要在单个主线程方法调用中进行迭代,因此运行循环不会关闭。如您所说,在您返回后,将调用deallocs DO。假定事务将所有操作(添加和删除)存储在某种形式的堆栈中,并保留它们,并且仅在事务结束时才进行处理。因此,您所看到的行为是预期的,如果您确实需要在单个运行循环迭代中清理引用,则可能需要自己明确地处理事务,而不是冲刷隐式事务,这可能导致性能低下或意外的副作用。 / p>

答案 2 :(得分:0)

让我猜测-您在项目中不使用ARC吗?

我尝试了几种方法来重现您对上述泄漏的主张,但找不到任何办法使之发生。

我在您的班级添加了标签:

@interface TestLayer: CALayer
@property (nonatomic, assign) int tag;
@end

@implementation TestLayer
-(void)dealloc
{
    NSLog(@"dealloc called for %d", self.tag);
}
@end

首先,我在完全相同的地方尝试了您的代码AppDelegate:

for (int i=0; i<10; i++) {
    @autoreleasepool {
        TestLayer* test = [TestLayer layer];
        test.tag = 100;
        CALayer* l = [CALayer layer];
        [test addSublayer:l];
        [l removeFromSuperlayer];
        l = nil;
        test = nil;
    }
}

它每次都会打印dealloc

然后,在我的第一个视图控制器中,我对其进行了少许修改:

- (void)viewDidLoad {
    [super viewDidLoad];

    tl = [TestLayer layer]; // its an ivar
    tl.tag = 100;
    for (int i=0; i<10; i++) {
      @autoreleasepool {
        TestLayer *l = [TestLayer layer];
        l.tag = i + 10;
        [tl addSublayer:l];
        [tl removeFromSuperlayer];  // tried this, also tried commenting it out
      }
    }
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        NSArray *sl = [self->tl.sublayers copy];
        for (TestLayer *l in sl) {
            [l removeFromSuperlayer];
        }
        self->tl = nil;
    });

}

我唯一可以得出的结论是您没有使用ARC,应该提一下。

PS:如果不使用ARC,我只能假设CALayer layer返回的保留计数为1的对象,并且该对象未自动释放。如果是这样,则需要显式发送一条release消息。