我在UIScrollView实现中泄漏了内存吗? [英] Am I leaking memory in my UIScrollView implementation?

查看:75
本文介绍了我在UIScrollView实现中泄漏了内存吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个使用UIScrollView滚动浏览一堆幻灯片的应用。当应用程序打开时,它会创建幻灯片并将它们传递给滚动视图。我的应用程序还有一个计时器,使UIScrollView滚动幻灯片。

I am working on an app which uses a UIScrollView to scroll through a bunch of slides. When the app opens, it creates the slides and passes them to the scroll view. My app also has a timer which causes the UIScrollView to scroll through the slides.

我还有一个设置按钮。当我按下该按钮时,它会打开设置面板。计时器无效并设置为 nil 。当设置面板打开时,用户可以更改应用程序主题,幻灯片和动画方向等内容。

I also have a settings button. When I press on that button, it opens the settings panel. The timer is invalidated and set to nil. When the settings panel opens, the user can change things like the application theme, the slides and the animation direction.

当关闭设置面板时,将应用设置:从UIScrollView中删除幻灯片,并为UIScrollView提供新的NSArray幻灯片以供使用。然后,UIScrollView以正确的顺序和方向布置新幻灯片。计时器被重置。

When the settings panel is closed, the settings are applied: The slides are removed from the UIScrollView and the UIScrollView is given a new NSArray of slides to work with. The UIScrollView then lays out the new slides in the proper order and direction. The timer is reset.

一旦大约每14-16次,应用程序在运行 dismissAndRestart 时崩溃( 关闭代码)。我不知道为什么。我以为我是在泄漏记忆,显然我是,但我看不到。

Once, approximately every 14-16 times, the app crashes while running dismissAndRestart (the "close" code). I'm not sure why. I thought I was leaking memory, and apparently I am, but I don't see where.

这是我的代码:

这在启动时运行:

    - (void)viewDidLoad {    
        [scrollView setPagingEnabled:YES];
    }

    - (void) viewWillAppear:(BOOL)animated{
        [self prepareViews];
        [self applyTheme];  
    }

    - (void) viewDidAppear:(BOOL)animated{
        [self createTimerWithInterval:kScrollInterval];
    }

将视图加载到滚动条的主力方法。 (它也删除了旧的):

- (void)loadViews:(NSArray *)views IntoScroller:(UIScrollView *)scroller withDirection:(NSString *)direction{
    [scrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    [scrollView setShowsHorizontalScrollIndicator: NO];
    [scrollView setShowsVerticalScrollIndicator:NO];
    scrollView.scrollsToTop = NO;

    if([direction isEqualToString:@"horizontal"]){
        scrollView.frame = CGRectMake(0, 0, 1024, 768);
        scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * [[NSNumber numberWithUnsignedInt:[views count]] floatValue], scrollView.frame.size.height);
    }else if([direction isEqualToString:@"vertical"]){
        scrollView.frame = CGRectMake(0, 0, 1024, 768);
        scrollView.contentSize = CGSizeMake(scrollView.frame.size.width, scrollView.frame.size.height * [[NSNumber numberWithUnsignedInt:[views count]] floatValue]);
    }

    for (int i=0; i<[[NSNumber numberWithUnsignedInt:[views count]] intValue]; i++) {

        [[[views objectAtIndex:[[NSNumber numberWithInt:i] unsignedIntValue]] view] setFrame:scrollView.frame];

        if([direction isEqualToString:@"horizontal"]){
            [[[views objectAtIndex:[[NSNumber numberWithInt:i] unsignedIntValue]] view] setFrame:CGRectMake(i * [[views objectAtIndex:[[NSNumber numberWithInt:i] unsignedIntValue]] view].frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height)];//CGRectMake(i * announcementView.view.frame.size.width, -scrollView.frame.origin.x, scrollView.frame.size.width, scrollView.frame.size.height)];
        }else if([direction isEqualToString:@"vertical"]){
            [[[views objectAtIndex:[[NSNumber numberWithInt:i] unsignedIntValue]] view] setFrame:CGRectMake(0, i * [[views objectAtIndex:[[NSNumber numberWithInt:i] unsignedIntValue]] view].frame.size.height, scrollView.frame.size.width, scrollView.frame.size.height)];
        }

        [scrollView addSubview:[[views objectAtIndex:[[NSNumber numberWithInt:i] unsignedIntValue]] view]];
    }
}

创建自动滚动的计时器:

#pragma mark -
#pragma mark Create the Timer to automate scrolling

- (void) createTimerWithInterval:(float)interval{
    self.timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(scrollWrapperForTimer) userInfo:nil repeats:YES];
}

计时器功能的包装函数。 (在NSUserDefault方向之前这是必要的,现在我不需要这个。)

#pragma mark -
#pragma mark Scroll Automatically Every N seconds

- (void) scrollWrapperForTimer{
    [self scrollToNewViewInDirection:kDirection];
}

- (void)scrollToNewViewInDirection:(NSString *)direction{
    if([[NSUserDefaults standardUserDefaults] boolForKey:@"animated_scrolling"] == YES){
        if([direction isEqualToString:@"horizontal"]){
            if([[NSNumber numberWithFloat:scrollView.contentOffset.x] compare:[NSNumber numberWithFloat:scrollView.contentSize.width - scrollView.frame.size.width]] == (NSOrderedAscending)){
                [scrollView scrollRectToVisible:CGRectMake(scrollView.contentOffset.x + scrollView.frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height) animated:YES];
            }else if([[NSNumber numberWithFloat:scrollView.contentOffset.x] compare:[NSNumber numberWithFloat:scrollView.contentSize.width - scrollView.frame.size.width]] == (NSOrderedSame)){
                [scrollView scrollRectToVisible:CGRectMake(0, 0, scrollView.frame.size.width, scrollView.frame.size.height) animated:YES];
            }
        }else if([direction isEqualToString:@"vertical"]){
            if([[NSNumber numberWithFloat:scrollView.contentOffset.y] compare:[NSNumber numberWithFloat:scrollView.contentSize.height - scrollView.frame.size.height]] == (NSOrderedAscending)){
                [scrollView scrollRectToVisible:CGRectMake(0, scrollView.contentOffset.y + scrollView.frame.size.height, scrollView.frame.size.width, scrollView.frame.size.height) animated:YES];
            }else if([[NSNumber numberWithFloat:scrollView.contentOffset.y] compare:[NSNumber numberWithFloat:scrollView.contentSize.height - scrollView.frame.size.height]] == (NSOrderedSame)){
                [scrollView scrollRectToVisible:CGRectMake(0, 0, scrollView.frame.size.width, scrollView.frame.size.height) animated:YES];
            }
        }
    }else {
        if([direction isEqualToString:@"horizontal"]){
            if([[NSNumber numberWithFloat:scrollView.contentOffset.x] compare:[NSNumber numberWithFloat:scrollView.contentSize.width - scrollView.frame.size.width]] == (NSOrderedAscending)){
                [scrollView scrollRectToVisible:CGRectMake(scrollView.contentOffset.x + scrollView.frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height) animated:NO];
            }else if([[NSNumber numberWithFloat:scrollView.contentOffset.x] compare:[NSNumber numberWithFloat:scrollView.contentSize.width - scrollView.frame.size.width]] == (NSOrderedSame)){
                [scrollView scrollRectToVisible:CGRectMake(0, 0, scrollView.frame.size.width, scrollView.frame.size.height) animated:NO];
            }
        }else if([direction isEqualToString:@"vertical"]){
            if([[NSNumber numberWithFloat:scrollView.contentOffset.y] compare:[NSNumber numberWithFloat:scrollView.contentSize.height - scrollView.frame.size.height]] == (NSOrderedAscending)){
                [scrollView scrollRectToVisible:CGRectMake(0, scrollView.contentOffset.y + scrollView.frame.size.height, scrollView.frame.size.width, scrollView.frame.size.height) animated:NO];
            }else if([[NSNumber numberWithFloat:scrollView.contentOffset.y] compare:[NSNumber numberWithFloat:scrollView.contentSize.height - scrollView.frame.size.height]] == (NSOrderedSame)){
                [scrollView scrollRectToVisible:CGRectMake(0, 0, scrollView.frame.size.width, scrollView.frame.size.height) animated:NO];
            }
        }
    }

}

在设置面板中按下完成按钮时会调用此方法。

#pragma mark -
#pragma mark Restart the program

- (void) dismissAndRestart{
    [self prepareViews];
    [self applyTheme];
    [self dismissModalViewControllerAnimated:YES];
    [self createTimerWithInterval:kScrollInterval];
}

这会创建正确的图像文件并将其加载到位:

#pragma mark -
#pragma mark Apply the theme to the main view

- (void) applyTheme{

    //Front panel
    UIImage *frontImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[[NSString stringWithFormat:@"%@_front", kTheme]description] ofType:@"png"]];  
    [self.overlayImage setImage:frontImage];
    [frontImage release];

    //Back Panel
    if([kTheme isEqualToString:@"walnut"]){
        [self.backgroundImage setHidden:NO];
        UIImage *backImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[[NSString stringWithFormat:@"%@_back", kTheme]description] ofType:@"png"]];    
        [self.backgroundImage setImage:backImage];
        [backImage release];            
    }else if([kTheme isEqualToString:@"metal"]){
        [self.backgroundImage setHidden:YES];
        [self.view setBackgroundColor: [UIColor scrollViewTexturedBackgroundColor]];
    }

    //Gabbai Button
    UIImage *gabbaiImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[[NSString stringWithFormat:@"%@_settings_button", kTheme]description] ofType:@"png"]];   
    [self.gabbaiButton setImage:gabbaiImage forState:UIControlStateNormal];
    [gabbaiImage release];  


}

需要的另一个包装函数待重构:

#pragma mark -
#pragma mark Slide View related

- (void) prepareViews{
    [self loadViews:[self createandReturnViews] IntoScroller:scrollView withDirection:kDirection];
}

创建要传递给滚动条的幻灯片数组:

//recreation of the views causes a delay each time the admin panel is closed.
- (NSArray*) createandReturnViews{

    NSMutableArray *announcements = [NSMutableArray array];
    NSArray *announcementsArray = [NSArray arrayWithObjects: @"Welcome to\nGabbai HD!",
                                   @"First slide",
                                   @"Second Slide",
                                   @"Third Slide",
                                   @"etc.",
                                   nil];
    for (int i = 0; i<[[NSNumber numberWithUnsignedInteger:[announcementsArray count]] intValue]; i++) {
        MBAnnouncementViewController *announcement = [[MBAnnouncementViewController alloc] initWithNibName:@"MBAnnouncementViewController" bundle:nil];
        [announcement setAnnouncementText:[announcementsArray objectAtIndex:[[NSNumber numberWithInt:i] unsignedIntegerValue]]];
        [announcements addObject:announcement];
        [announcement release];
    }

    return announcements;
}

为什么我的设置面板会在关闭时崩溃应用程序?

Why would my settings panel be crashing the app on close?

推荐答案

您展示的代码有几个alloc调用。在每个之后,您设置一个属性,然后释放。我假设该属性声明为 retain

The code you show has a few alloc calls. After each one, you set a property, and release. I assume that the property is declared as retain.

如果是这样,那么最终需要释放这些属性。通常在dealloc消息实现中。

If so, then eventually, you need to release these properties. Usually in the dealloc message implementation.

具有保留属性的类需要像这样的dealloc

Your classes with retained properties need a dealloc like this

 -(void) dealloc() {
    self.prop = nil; // calls release
    self.prop2 = nil;
    [super dealloc];
 }

如果您的房产声明如此

  @property (nonatomic, retain) Type* prop;

然后当您使用设置 message或 self.prop 语法,前一个值已经调用了它。如果你只是使用 prop = newVal; ,那么你是直接访问该字段而不是调用 set 消息,所以不会调用release。

Then when you reassign the property with either the set message or self.prop syntax, the previous value has release called on it. If you just use prop = newVal;, then you are accessing the field directly and not calling the set message, so release will not be called.

通常,总是使用 set 消息或点语法,这样你就不会需要担心它 - 这是你将属性声明为保留的主要原因,所以要利用它。

Generally, always use the set message or dot syntax, so that you don't need to worry about it -- it's the main reason you declare the property as retain, so take advantage of it.

这篇关于我在UIScrollView实现中泄漏了内存吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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