[英]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.