内存消耗增加但是泄漏未检测到泄漏时,如何处理iOS中的泄漏?

我正在开发一个iOS ARC应用程序(更多的代码可以应要求提供),而且之前我正在制作和丢弃大量的图像。 我认为我的某个地方仍然有一个图像的引用,即使我叫removeFromSuperview,并试图删除所有引用不再使用的图像。 我尝试了Leaks,Leaks报告说,随着时间的推移,内存使用量大致呈线性增长,大约从17M左右开始。

我将所有对图像的引用都replace为实例variables,因此它们将占用一个小的,有限的和固定的内存量,并且转换用于钟针的图像,而不是摆脱这些图像。 不幸的是,这导致内存使用量逐渐增加,从5M开始,而不是17M,但是另一个相同的问题,只是转化为更好的起点。

我的代码的修剪版本如下。 你能告诉我什么是泄漏(或“泄漏”,因为泄漏没有表明泄漏)关于这个,以及如何我可以保持接近代码使用时的内存边界启动?

谢谢,

- (void) renderScreen { int height = floor([[UIScreen mainScreen] bounds].size.height + .4); int width = floor([[UIScreen mainScreen] bounds].size.width + .4); if (height == 2048 || height == 2008 || height == 1024 || height == 1004 || height == 984) { if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) { _backgroundImage = [UIImage imageNamed:@"Background-Default-Portrait.png"]; } else { _backgroundImage = [UIImage imageNamed:@"Background-Default-Landscape.png"]; } } else if (height == 1536 || height == 768 || height == 748 || height == 728) { _backgroundImage = [UIImage imageNamed:@"Background-Default-Landscape.png"]; } else if (height == 1136 || height == 1116 || height == 1096) { if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) { _backgroundImage = [UIImage imageNamed:@"Background-Default-568.png"]; } else { _backgroundImage = [UIImage imageNamed:@"Background-Default-Rotated-568.png"]; } } else if (height == 960 || height == 940 || height == 920 || height == 480 || height == 460 || height == 440) { if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) { _backgroundImage = [UIImage imageNamed:@"Background-Default.png"]; } else { _backgroundImage = [UIImage imageNamed:@"Background-Default-Rotated.png"]; } } else if ((height == 640 || height == 620 || height == 600) && (width == 1136 || width == 1116 || width == 1096)) { _backgroundImage = [UIImage imageNamed:@"Background-Rotated-568.png"]; } else if ((height == 640 || height == 620 || height == 600 || height == 320 || height == 300 || height == 280) && (width == 960 || width == 940 || width == 920 || width == 480 || width == 470 || width == 410)) { if (UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) { _backgroundImage = [UIImage imageNamed:@"Background-Default-Portrait.png"]; } else { _backgroundImage = [UIImage imageNamed:@"Background-Default-Rotated.png"]; } } else { _backgroundImage = [UIImage imageNamed:@"Background-Default-Portrait.png"]; } if (!UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) { int juggle = height; height = width; width = juggle; } NSUInteger centerX = width * .5; NSUInteger centerY = height * .5; _containerRect = CGRectZero; _containerRect.size = [[UIScreen mainScreen] bounds].size; self.view.backgroundColor = [UIColor colorWithPatternImage:_backgroundImage]; _backgroundView = [[UIImageView alloc] initWithImage:_backgroundImage]; [self.view addSubview:_backgroundView]; if (_changed) { _containerView = [[UIView alloc] initWithFrame:_containerRect]; } double timeStampSeconds = [[NSDate date] timeIntervalSince1970]; double hours = fmod(timeStampSeconds / 86400, 24); double minutes = fmod(timeStampSeconds / 3600, 60); double seconds = fmod(timeStampSeconds, 60); NSLog(@"Milliseconds: %lf, Hours: %.0f, minutes: %.0f, seconds: %.0f", timeStampSeconds * 1000.0, hours, minutes, seconds); [_containerView removeFromSuperview]; _containerView = [[UIView alloc] initWithFrame:_containerRect]; _hourHandImage = [UIImage imageNamed:@"hour-hand.png"]; _hourHandView = [[UIImageView alloc] initWithImage:_hourHandImage]; _hourHandImage = [UIImage imageNamed:@"hour-hand.png"]; _hourHandView = [[UIImageView alloc] initWithImage:_hourHandImage]; [self.view addSubview:_hourHandView]; _hourHandView.layer.anchorPoint = CGPointMake(0.5f, 0.5f); _hourTransform = CGAffineTransformMakeTranslation(centerX, centerY); _hourTransform = CGAffineTransformTranslate(_hourTransform, -17, -127); _hourTransform = CGAffineTransformRotate(_hourTransform, hours / 12.0 * M_PI * 2.0); _minuteTransform = CGAffineTransformMakeTranslation(centerX, centerY); _minuteTransform = CGAffineTransformTranslate(_minuteTransform, -10, -182); _minuteTransform = CGAffineTransformRotate(_minuteTransform, minutes / 60.0 * M_PI * 2.0); _hourHandView.transform = _hourTransform; _minuteHandImage = [UIImage imageNamed:@"minute-hand.png"]; _minuteHandView = [[UIImageView alloc] initWithImage:_minuteHandImage]; _minuteHandView.transform = _minuteTransform; [self.view addSubview:_minuteHandView]; _minuteTransform = CGAffineTransformRotate(_minuteTransform, minutes / 60.0 * M_PI * 2.0); _secondHandImage = [UIImage imageNamed:@"second-hand.png"]; _secondTransform = CGAffineTransformMakeTranslation(centerX, centerY); _secondTransform = CGAffineTransformTranslate(_secondTransform, -10, -189); _secondTransform = CGAffineTransformRotate(_secondTransform, seconds / 60.0 * M_PI * 2.0); _secondHandView = [[UIImageView alloc] initWithImage:_secondHandImage]; _secondHandView.transform = _secondTransform; [self.view addSubview:_secondHandView]; } 

– 编辑 –

我已经将这个方法重构为两个方法,一个用于初始显示,另一个用于增量更新。 看来增量更新方法只被调用一次,因为时钟被冻结,签名日志语句只被调用一次。

我现在有更新部分:

 - (void)render { [self renderScreenInitial]; [NSTimer scheduledTimerWithTimeInterval:0.002 target:self selector:@selector(renderingTimer:) userInfo:nil repeats:YES]; } - (void) renderScreenIncremental { int height = floor([[UIScreen mainScreen] bounds].size.height + .4); int width = floor([[UIScreen mainScreen] bounds].size.width + .4); if (!UIInterfaceOrientationIsPortrait([UIApplication sharedApplication].statusBarOrientation)) { int juggle = height; height = width; width = juggle; } double decibelAngle = M_PI / 4 + (_decibel / 60) * M_PI / 2; double decibelAngleDifference = decibelAngle - _previousDecibelAngle; _previousDecibelAngle = decibelAngle; NSLog(@"%lf %lf", _decibel, decibelAngle); _decibelNeedleView = [[UIImageView alloc] initWithImage:_decibelNeedle]; // CGAffineTransform decibelTransform = CGAffineTransformMakeTranslation(centerX, centerY); CGAffineTransform decibelTransform = CGAffineTransformMakeRotation(decibelAngleDifference); decibelTransform = CGAffineTransformTranslate(decibelTransform, sin(decibelAngle - .06) * -298, -cos(decibelAngle - .06) * 298); _decibelNeedleView.transform = decibelTransform; [self.view addSubview:_decibelNeedleView]; double timestampSeconds = [[NSDate date] timeIntervalSince1970]; double timestampSecondsDifference = timestampSeconds - _previousTimestampSeconds; _previousTimestampSeconds = timestampSeconds; double hoursDifference = fmod(timestampSecondsDifference / 86400, 24); double minutesDifference = fmod(timestampSecondsDifference / 3600, 60); double secondsDifference = fmod(timestampSecondsDifference, 60); NSLog(@"Milliseconds: %lf, Hours: %.0f, minutes: %.0f, seconds: %.0f", timestampSecondsDifference * 1000.0, hoursDifference, minutesDifference, secondsDifference); _hourHandView.transform = CGAffineTransformMakeRotation(hoursDifference); _minuteHandView.transform = CGAffineTransformMakeRotation(minutesDifference); _secondHandView.transform = CGAffineTransformMakeRotation(secondsDifference); } -(void)renderingTimer:(NSTimer *)timer { [self renderScreenIncremental]; } 

我感谢帮助。 你看到为什么它应该更新显示一次,然后不继续保持更新?

谢谢,

首先,您应该使用“乐器”中的“分配”工具来确定正在分配的内容,而不是正在发布的内容。 我build议你想要WWDC 2012videoiOS应用程序性能:内存 ,这表明了这一点,除其他事情。

使用仪器的分配工具,你不会猜测什么是分配和不被释放,而是你可以使用heapshot /世代来确定被分配的实际对象,而不是在你select的时间范围内释放。 您甚至可以钻入并查看有问题的对象最初分配的位置。

除此之外,看看你的代码,看起来你正在删除并重新添加容器,但没有任何东西进入容器。 而且你正在增加小时/分钟/秒的手,但是不要删除它们。 也许你打算把这些添加到容器?

另外,你的代码并没有描述_changed做了什么,但是如果是YES ,那么你实际上正在创build一个_containerView ,将它从它的_containerView视图中移除(即使它从未被添加到_containerView视图中),然后丢弃它并创build另一个_containerView 。 很奇怪。

我也从这个代码推断,你会多次调用renderScreen 。 如果这是真的,那么我可能会build议从视图的“更改”(调整transform值)中分离视图内容的“创build”(添加背景,各种图像等)。 如果可能的话,你应该添加一次你的视图,然后在更新时尽量减less(改变变换)。

使用您正在使用的API的内存逐渐增加是预期的行为。 [UIImage imageNamed:] 将caching图像 。 如果您想要testingcaching刷新时是否释放这些内容,将导致发生内存警告。 您可以在模拟器中进入Hardware菜单并selectSimulate Memory Warning

您也可以使用以下代码以编程方式在设备上执行此操作:

 [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification object:[UIApplication sharedApplication]]; 

您不应该使用此代码运送生产应用程序