我花了一些时间来理解代码中的一些大内存泄漏,因此在简化代码后剩下的是:
@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引用了什么,我如何说服它释放它?
最佳答案
让我猜测-您在项目中不使用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
消息。