内存释放问题 - UIImages 的 NSArray

发布于 2024-10-21 22:42:51 字数 5052 浏览 1 评论 0原文

我的记忆力有问题。我可能不明白从导航控制器中弹出当前 UIViewcontroller 是如何工作的。但我认为它会释放它的内存,至少在内存不足时。就像调用dealloc方法一样。我有一个继承自 UIViewController 的类。我基本上是在做一个 png 动画序列。我无法使用animationImages,因为我需要一些交叉淡入淡出。不管怎样,当我观察活动监视器并打开页面(观察内存增加了 30MB)时,我就会弹出它。内存会减少几 MB,但不会减少全部。所以这种情况会一直发生,直到应用程序崩溃。泄漏检测器未显示任何泄漏的物体。我想我正在照顾它。如果有帮助的话我可以粘贴我的代码。关于 NavigationController 和发布 UIViewControllers 有什么我不明白的地方吗?

这是代码:

-(void)addImage:(NSString*)imageName{
    if (!images){
        images = [[NSMutableArray alloc] init];
    }

//    UIImage*image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle]  pathForResource:[imageName stringByReplacingOccurrencesOfString:@".png" withString:@""] ofType:@"png"]];
    [images addObject:imageName];
  //  [image release];
}

-(UIImage*)getImage:(NSString*)imageName{
    return [UIImage imageNamed:imageName];
}

// left pad the image number with 0
-(NSString*)formatImageName:(int)num andName:(NSString*)imgName{
    NSString* imgNum = [NSString stringWithFormat:@"%i",num];
    if (num < 10){
        imgNum = [NSString stringWithFormat:@"0%i",num];
    }
    return [NSString stringWithFormat:imgName,imgNum];
}
// Returns a UIImageView that contains the next image to display
- (UIImageView *)nextImageView
{
    if(runOnce && hasRunOnce && pictureIndex == 0){
        return nil;
    }
    // get the image at the next index
    UIImage *image = [self getImage:[images objectAtIndex:pictureIndex]];
    ++pictureIndex; // increment the index




    // create an image view for the image
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    //[image release];

    // resize the image to fill the screen without distorting
    imageView.frame = rotator.frame;

    imageView.autoresizesSubviews = NO;
    [imageView setContentMode:UIViewContentModeCenter];


    // Makes the image move proportionally in any direction if the
    // bounds of the superview change when the iPhone is rotated.
    imageView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
                                  UIViewAutoresizingFlexibleRightMargin |                          
                                  UIViewAutoresizingFlexibleTopMargin |                            
                                  UIViewAutoresizingFlexibleBottomMargin);

    if(pictureIndex > images.count -1){
        pictureIndex = 0;
    }

    hasRunOnce = YES;
    return imageView;
}

- (void)timerFired:(NSTimer *)_timer{
    nextImageView = [self nextImageView];
    if(nextImageView){
        [self.view addSubview:nextImageView];
        nextImageView.alpha = 0.0;

        //NSLog(@"timerFired - image no: %i", pictureIndex);

        // begin animation block
        [UIView beginAnimations:nil context:nextImageView];
        [UIView setAnimationDuration:animationDuration]; // set the animation length
        [UIView setAnimationDelegate:self]; // set the animation delegate

        // call the given method when the animation ends
        [UIView setAnimationDidStopSelector:@selector(transitionFinished:finished:context:)];

        // make the next image appear with the chosen effect
        [nextImageView setAlpha:1.0]; // fade in the next image
        [currentImageView setAlpha:0.0]; // fade out the old image

        [UIView commitAnimations];
    } else {
        [timer invalidate];
        timer = nil;
    }

}

// called when the image transition animation finishes
- (void)transitionFinished:(NSString *)animationId finished:(BOOL)finished context:(void *)context
{
    [currentImageView removeFromSuperview]; // remove the old image
    currentImageView.image = nil;
    [currentImageView release]; // release the memory for the old image
    currentImageView = context; // assign the new image
}

-(void)startRotator{
    pictureIndex = 0; // reset the index

    currentImageView = [self nextImageView]; // load the first image
    [self.view addSubview:currentImageView]; // add the image to the view

    timer = [NSTimer scheduledTimerWithTimeInterval:scheduledInterval 
                                             target:self
                                           selector:@selector(timerFired:) 
                                           userInfo:nil 
                                            repeats:YES];
}

-(void)stopRotator{
    NSLog(@"ImageRotator - StopRotator Called");
    [timer invalidate];
    timer = nil; // set timer to nil
    [currentImageView removeFromSuperview]; // remove the current image
    currentImageView.image = nil;

    [currentImageView release];
    currentImageView = nil;
    if(nextImageView){
        [nextImageView removeFromSuperview]; // remove the current image
        nextImageView.image = nil;
        //[nextImageView release];
        nextImageView = nil;
    }
}

// called after this controller's view was dismissed, covered or otherwise hidden
- (void)viewWillDisappear:(BOOL)animated
{   
    [self stopRotator];
    [images release];
    images = nil;
    [super viewWillDisappear:animated];
}

I am having a memory problem. I may not understand how popping the current UIViewcontroller from the navigationcontroller works. But I would think that it would release it's memory, atleast when it's running low. Like call the dealloc method. I have a class that inherits from UIViewController. I am basically doing an png animation sequence. I can't use animationImages because I need some cross fade. Anyways, when I watch the Activity Monitor and I open the page (watch the memory rise by 30MBs) then I pop it. The memory goes down by a few MBs but not by the full amount. So this keeps happening until the app crashes. The Leak detector doesn't show any leaked objects. I think I am taking care of it. I can paste my code if that would help. Is there something I don't understand about NavigationController and releasing UIViewControllers?

Here is the code:

-(void)addImage:(NSString*)imageName{
    if (!images){
        images = [[NSMutableArray alloc] init];
    }

//    UIImage*image = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle]  pathForResource:[imageName stringByReplacingOccurrencesOfString:@".png" withString:@""] ofType:@"png"]];
    [images addObject:imageName];
  //  [image release];
}

-(UIImage*)getImage:(NSString*)imageName{
    return [UIImage imageNamed:imageName];
}

// left pad the image number with 0
-(NSString*)formatImageName:(int)num andName:(NSString*)imgName{
    NSString* imgNum = [NSString stringWithFormat:@"%i",num];
    if (num < 10){
        imgNum = [NSString stringWithFormat:@"0%i",num];
    }
    return [NSString stringWithFormat:imgName,imgNum];
}
// Returns a UIImageView that contains the next image to display
- (UIImageView *)nextImageView
{
    if(runOnce && hasRunOnce && pictureIndex == 0){
        return nil;
    }
    // get the image at the next index
    UIImage *image = [self getImage:[images objectAtIndex:pictureIndex]];
    ++pictureIndex; // increment the index




    // create an image view for the image
    UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
    //[image release];

    // resize the image to fill the screen without distorting
    imageView.frame = rotator.frame;

    imageView.autoresizesSubviews = NO;
    [imageView setContentMode:UIViewContentModeCenter];


    // Makes the image move proportionally in any direction if the
    // bounds of the superview change when the iPhone is rotated.
    imageView.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin |
                                  UIViewAutoresizingFlexibleRightMargin |                          
                                  UIViewAutoresizingFlexibleTopMargin |                            
                                  UIViewAutoresizingFlexibleBottomMargin);

    if(pictureIndex > images.count -1){
        pictureIndex = 0;
    }

    hasRunOnce = YES;
    return imageView;
}

- (void)timerFired:(NSTimer *)_timer{
    nextImageView = [self nextImageView];
    if(nextImageView){
        [self.view addSubview:nextImageView];
        nextImageView.alpha = 0.0;

        //NSLog(@"timerFired - image no: %i", pictureIndex);

        // begin animation block
        [UIView beginAnimations:nil context:nextImageView];
        [UIView setAnimationDuration:animationDuration]; // set the animation length
        [UIView setAnimationDelegate:self]; // set the animation delegate

        // call the given method when the animation ends
        [UIView setAnimationDidStopSelector:@selector(transitionFinished:finished:context:)];

        // make the next image appear with the chosen effect
        [nextImageView setAlpha:1.0]; // fade in the next image
        [currentImageView setAlpha:0.0]; // fade out the old image

        [UIView commitAnimations];
    } else {
        [timer invalidate];
        timer = nil;
    }

}

// called when the image transition animation finishes
- (void)transitionFinished:(NSString *)animationId finished:(BOOL)finished context:(void *)context
{
    [currentImageView removeFromSuperview]; // remove the old image
    currentImageView.image = nil;
    [currentImageView release]; // release the memory for the old image
    currentImageView = context; // assign the new image
}

-(void)startRotator{
    pictureIndex = 0; // reset the index

    currentImageView = [self nextImageView]; // load the first image
    [self.view addSubview:currentImageView]; // add the image to the view

    timer = [NSTimer scheduledTimerWithTimeInterval:scheduledInterval 
                                             target:self
                                           selector:@selector(timerFired:) 
                                           userInfo:nil 
                                            repeats:YES];
}

-(void)stopRotator{
    NSLog(@"ImageRotator - StopRotator Called");
    [timer invalidate];
    timer = nil; // set timer to nil
    [currentImageView removeFromSuperview]; // remove the current image
    currentImageView.image = nil;

    [currentImageView release];
    currentImageView = nil;
    if(nextImageView){
        [nextImageView removeFromSuperview]; // remove the current image
        nextImageView.image = nil;
        //[nextImageView release];
        nextImageView = nil;
    }
}

// called after this controller's view was dismissed, covered or otherwise hidden
- (void)viewWillDisappear:(BOOL)animated
{   
    [self stopRotator];
    [images release];
    images = nil;
    [super viewWillDisappear:animated];
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

灼疼热情 2024-10-28 22:42:51

首先,内存会突然增加,因为您将所有图像存储在数组中,为此我建议您将它们存储在文档目录和数组中所有图像的地址(字符串数组)中。
另外,减少内存使用的一个想法是,

myImageView.image = nil;

如果您使用任何 imageView 来显示图像,则在不显示任何图像时使用上面的行。
需要注意的一件事是,当应用程序内存不足时,它会调用

- (void)didReceiveMemoryWarning { 
}

not

- (void)dealloc {
}

我建议您阅读内存管理

另外,您可以使用 Instruments 来检查内存泄漏

first of all there is an abrupt increase in memory because you are storing all that images in your array, for that I would suggest you to store them in document directory and address of all those in array (array of stings).
Also one idea to decrease the memory use would be to use

myImageView.image = nil;

if you are using any imageView to display image, use above line whenever you are not displaying any image.
One thing to mind is that when the application go out of memory then it calls

- (void)didReceiveMemoryWarning { 
}

not

- (void)dealloc {
}

I would suggest you to read memory management

Also you can use Instruments to check for memory leaks

梦在深巷 2024-10-28 22:42:51

我认为你应该更改的第一件事是下面的代码

- (void)timerFired:(NSTimer *)_timer
{
// all your code 
 .......
 .......
//add these two lines at bottom

[nextImageView release];
 //I think the leak is because you are adding imageViews and not releasing it .... when ever you add any subview to any view its retain count is incleased.
currentImageView.image = nil;
//also you are not using current view any more because you are setting its alfa to 0 so you can set its image to nil.

}

The first thing that I think you should change is the code below

- (void)timerFired:(NSTimer *)_timer
{
// all your code 
 .......
 .......
//add these two lines at bottom

[nextImageView release];
 //I think the leak is because you are adding imageViews and not releasing it .... when ever you add any subview to any view its retain count is incleased.
currentImageView.image = nil;
//also you are not using current view any more because you are setting its alfa to 0 so you can set its image to nil.

}

深海蓝天 2024-10-28 22:42:51

内存问题最终出在 ImageNamed 方法上。一旦我使用另一种方法用以下方法覆盖它,一切似乎都工作正常。

@implementation UIImage(imageNamed_Hack)
+ (UIImage *)imageNamed:(NSString *)name {
    return [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name ofType:nil]];
}
@end

The memory problem ended up being the ImageNamed method. Once I used the other method overrode it with the following, everything seemed to work fine.

@implementation UIImage(imageNamed_Hack)
+ (UIImage *)imageNamed:(NSString *)name {
    return [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name ofType:nil]];
}
@end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文