簡體   English   中英

我在UIScrollView實現中泄漏了內存嗎?

[英]Am I leaking memory in my UIScrollView implementation?

我正在開發一個使用UIScrollView滾動瀏覽一堆幻燈片的應用程序。 當應用打開時,它將創建幻燈片並將其傳遞到滾動視圖。 我的應用程序還有一個計時器,使UIScrollView滾動幻燈片。

我也有一個設置按鈕。 當我按該按鈕時,它將打開設置面板。 計時器無效並設置為nil 當設置面板打開時,用戶可以更改應用程序主題,幻燈片和動畫方向等內容。

關閉設置面板后,將應用設置:將幻燈片從UIScrollView中移除,並為UIScrollView提供新的NSArray幻燈片以供使用。 然后,UIScrollView按正確的順序和方向布置新幻燈片。 計時器重置。

大約每14-16次,應用程序在運行dismissAndRestart (“關閉”代碼)時崩潰。 我不知道為什么。 我以為我是在泄漏記憶,顯然我是,但我不知道在哪里。

這是我的代碼:

這在啟動時運行:

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

為什么我的設置面板會在關閉時使應用程序崩潰?

您顯示的代碼有幾個alloc調用。 在每一個之后,你設置一個屬性,然后釋放。 我假設該屬性被聲明為retain

如果是這樣,那么最終,您需要釋放這些屬性。 通常在dealloc消息實現中。

具有保留屬性的類需要這樣的dealloc

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

如果您的屬性是這樣聲明的

  @property (nonatomic, retain) Type* prop;

然后,當您使用set message或self.prop語法重新分配屬性時,先前的值會在其上調用release。 如果只使用prop = newVal; ,那么您將直接訪問該字段而不調用set消息,因此將不會調用release。

通常,始終使用set message或dot語法,這樣您就不必擔心 - 這是將屬性聲明為retain的主要原因,因此請充分利用它。

我沒有讀過你的代碼,但可能是你試圖發布一個已經發布的對象。 我建議您在開發時打開僵屍,但在編譯發行版本時應記住將其關閉,因為啟用僵屍時不會釋放內存。 在調試時非常有用,如果你是代碼試圖訪問不再存在的對象。 谷歌“啟用僵屍xcode”或類似的東西

您還可以使用xcode analyzer使用window + shift + a來查找內存泄漏。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM