当内存消耗上升但Leaks无法检测到泄漏时,如何处理iOS中的泄漏? [英] How to deal with leaks in iOS when memory consumption climbs but Leaks does not detect a leak?

查看:133
本文介绍了当内存消耗上升但Leaks无法检测到泄漏时,如何处理iOS中的泄漏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个iOS ARC应用程序(可应要求提供更多代码),并且早些时候我正在生产和丢弃大量图像.我以为即使我调用removeFromSuperview并尝试删除所有不再使用的图像的引用,我仍然在某个地方有图像的引用.我尝试了Leaks,Leaks报告内存使用量随时间大致呈线性增长,大约为1700万.

I am working on an iOS ARC app (more code available on request), and earlier I was producing and discarding large numbers of images. I thought that I somewhere still had a reference to images, even if I called removeFromSuperview and tried to remove all references to no-longer-used images. I tried Leaks, and Leaks reported roughly linear increase in memory usage over time, starting around 17M.

我仔细检查了所有对图像的引用,并将它们替换为实例变量,因此它们将占用少量,有限且固定的内存,然后转换而不是摆脱用于钟针的图像.不幸的是,随着时间的推移,这导致内存使用量逐渐增加,从5M开始而不是17M,但是其他问题相同,只是转化为一个更好的起点.

I went through and replaced all references to images to be instance variables, so they would take a small, finite, and fixed amount of memory, and transformed, instead of getting rid of, the images used for clock hands. This, unfortunately, resulted in slowly increasing memory usage over time, starting at 5M rather than 17M, but otherwise the same problem, simply translated to a better starting point.

下面是我的代码的精简版本.您能告诉我什么是泄漏的(或伪泄漏",因为Leaks并不表明泄漏),以及我如何保持它在代码启动时所使用的内存范围内?

A trimmed version of my code is below. Could you tell me what is leaky (or "pseudo-leaky" as Leaks did not indicate a leak) about this and how I can stay close to the memory bounds the code uses when it starts up?

谢谢

- (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];
}

-编辑-

我将一种方法重构为两种方法,一种用于初始显示,另一种用于增量更新.似乎增量更新方法仅被调用一次,因为时钟被冻结并且签名记录语句被精确地调用了一次.

I have refactored the one method into two methods, one for initial display, and one for incremental updates. It appears that the incremental update method is only called once, as the clock is frozen and the signature logging statement is called exactly once.

我现在有更新部分:

- (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];
}

我感谢您的帮助.您知道为什么它应该更新一次显示然后不继续更新吗?

I appreciate the help. Do you see why it should update the display once and then not continue to keep it updated?

谢谢

推荐答案

首先,您应该使用Instruments中的分配"工具来确定正在分配和未释放的内容.我建议您要WWDC 2012视频 iOS应用性能:内存 ,除其他外,这证明了这一点.

First and foremost, you should use Allocations tool in Instruments to identify what's being allocated and not being released. I'd suggest you want WWDC 2012 video iOS App Performance: Memory, which demonstrates this, amongst other things.

使用乐器的分配工具,您将不会猜测正在分配和未释放的内容,而是可以使用堆放/生成来识别在选择的某个时间范围内正在分配但未释放的实际对象. .您甚至可以深入研究,看看有问题的对象最初是在哪里分配的.

Using Instrument's Allocations tool, you won't be guessing what's being allocated and not being released, but rather you can use heapshots/generations to identify the actual objects that are being allocated and not being released within some time range of your picking. You can even drill in and see where the offending objects were originally allocated.

此外,浏览一下代码,似乎您正在删除并重新添加容器,但是没有任何内容进入容器.而且您要添加时针/分针/秒针,但不要移开它们.也许您打算将它们添加到容器中?

With that aside, glancing at your code, it appears that you're removing and re-adding the container, but nothing is going into the container. And you're adding the hour/minute/second hands, but never removing them. Perhaps you meant to add those to the container?

此外,您的代码没有描述_changed的功能,但是如果它是YES,则您实际上是在创建_containerView,并将其从其超级视图中删除(即使从未将其添加到超级视图中) ),然后将其丢弃并创建另一个_containerView.很奇怪.

Also, your code doesn't describe what _changed does, but if that's YES, then you're effectively creating a _containerView, removing it from its superview (even though it was never added to a superview), and then discarding it and creating another _containerView. Very strange.

我还从这段代码中推断出您将多次调用renderScreen.如果是这样,那么我建议将视图内容的创建"(添加背景,各种图像等)与视图的更改"(transform值的调整)分开.如果可能的话,您应该添加一次视图,然后在更新时进行最少的改动(更改转换).

I'm also inferring from this code that you'll be calling renderScreen many times. If that's true, then I might suggest separating the "creation" of the view stuff (adding background, the various images, etc.) from the "change" of the view (the adjustment of the transform values). If possible, you should add your views once, and then do the bare minimum (changing the transforms) upon updates.

这篇关于当内存消耗上升但Leaks无法检测到泄漏时,如何处理iOS中的泄漏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆