具有多个视图/子视图的iphone应用程序:内存未被释放

时间:2009-09-27 06:26:46

标签: iphone memory-management uiviewcontroller nib dealloc

我有一个iPhone应用程序,它基于一个explained in this link(基本上是一个使用ViewController方法加载/删除其他视图的主displayView)在框架中加载成功的视图。在我的应用程序中我正在使用NIB (示例链接使用编码视图),因此我的每个ViewControllers都有其随附的笔尖。

仪器中的调试显示无泄漏,但如果我进入/离开一个部分(带有View.xib的ViewController),则nib保留在内存中,因此在几个输入/输出内存开始累积之后。

我知道nib没有被卸载,因为一个几乎是以编程方式创建的(IB中没有东西),而另一个确实在IB中创建了图像和按钮。首先加载大的,然后加载小的。您可以预期仪器的分配会减少。

我该如何防止这种情况?

我的结构如下,下面有一些评论:

`MyAppDelegate.h`

#import <UIKit/UIKit.h>

@class RootViewController;

@interface MyAppDelegate : NSObject <UIApplicationDelegate> {
 UIWindow *window;
 RootViewController *viewController;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;
@property (nonatomic, retain) IBOutlet RootViewController *viewController;

-(void) displayView:(int)intNewView;

@end

`MyAppDelegate.m`

#import "MyAppDelegate.h"
#import "RootViewController.h"

@implementation MyAppDelegate

@synthesize window;
@synthesize viewController;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
 [window addSubview:viewController.view];
 [window makeKeyAndVisible];
 return YES;
}

- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
}

-(void) displayView:(int)intNewView {
 [viewController displayView:intNewView];
}

- (void)dealloc {
 [viewController release];
 [window release];
 [super dealloc];
}

@end

此控制器处理子视图加载/删除:

`RootViewController.h`

#import <UIKit/UIKit.h>

@interface RootViewController : UIViewController {
}

- (void) displayView:(int)intNewView;

@end

`RootViewController.m`

#import "RootViewController.h"
#import "ViewController.h"

@implementation RootViewController

UIViewController *currentView;

- (void) displayView:(int)intNewView {
 NSLog(@"%i", intNewView);
 [currentView.view removeFromSuperview];
 [currentView release];
 switch (intNewView) {
  case 1:
   currentView = [[ViewController alloc] initWithNibName:@"View" bundle:nil];
   break;
 }

 [self.view addSubview:currentView.view];
}

- (void)viewDidLoad {
 currentView = [[ViewController alloc]
   initWithNibName:@"View" bundle:nil];
 [self.view addSubview:currentView.view];
 [super viewDidLoad];
}

- (void)dealloc {
 [currentView release];
 [super dealloc];
}

@end

我会有case与“详细信息”ViewControllers一样多(现在我有3 case,但这会增长到10或更多)。这种结构的目的是轻松地从应用程序的一个“部分”移动到另一个部分(NavBar控制器或TabBar控制器不适合我的特定需求)。

`ViewController.h`

// Generic View Controller Example

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController {
 UIImageView *_image1;
 UIImageView *_image2;
 NSTimer *_theTimer;
}

@property (nonatomic, retain) IBOutlet UIImageView *image1;
@property (nonatomic, retain) IBOutlet UIImageView *image2;
@property (nonatomic, retain) NSTimer *theTimer;

@end

`ViewController.m`

#import "ViewController.h"
#import "MyAppDelegate.h"

@synthesize image1 = _image1, image2 = _image2, theTimer = _theTimer;

- (void)loadMenu {
 [self.theTimer invalidate];
 self.theTimer = nil;
 MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
 [appDelegate displayView:2];
} 

-(void)setView:(UIView*)aView {
 if (!aView){
  self.image1 = nil;
  self.image2 = nil;
 }
 [super setView:aView];
}

- (void)viewDidLoad {
 //some code
 [super viewDidLoad];
}

- (void)viewDidUnload {
 self.image1 = nil;
 self.image2 = nil;
}

- (void)dealloc {
 NSLog(@"dealloc called");
 [self.theTimer invalidate];
 [self.theTimer release];
 [self.image1 release];
 [self.image2 release];
 [super dealloc];
}

请注意NSLog中的dealloc。这是被调用的(我可以在控制台中看到它)但是nib所需的内存没有被释放(仪器显示在离开一个部分时内存分配增加,因为加载了新的nib)。

任何帮助将不胜感激。我已经尝试了一百万种不同的东西,我无法卸下笔尖。

2 个答案:

答案 0 :(得分:6)

经过一百万次尝试后,我终于遇到了this forum

它声明:

  

显然,使用imageNamed将IB中分配的图像加载到图像视图中。 imageNamed以一种使它们无法加载的方式缓存图像。您可以使用viewDidLoadinitWithContentsOfFile中加载图片,然后将它们分配给视图。

在其他地方,我读过imageNamed is the devil,所以我宁愿不让我的图像加载。

(顺便说一句,这是我正在使用的iPhone OS 3.1)

我最终的结果是将UIImageView保留在IB中,但空值为.image。修改后的代码类似于:

- (void)viewDidLoad {
    NSString *path = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], @"myImageThatBeforeWasAValueinIB.jpg"];
    UIImage *image = [UIImage imageWithContentsOfFile:path];
    outlet.image = image;
    // do the rest of my stuff as it was
    [super viewDidLoad];
}

- (void)dealloc {
    outlet.image = nil;
    [outlet release], outlet = nil;
    [super dealloc];
}

现在一切都像魅力一样!当我卸下笔尖并且出现内存警告时,内存会恢复。

如果你IBOutletsUIImageView并且内存是一个值得关注的问题(我猜它总是如此),那么你可以在IB中设计你想要的所有内容以及何时连接它们到出口,删除IB中的图像参考并从代码创建它。 IB非常适合布置您的应用。我不得不通过代码完成所有这些事情,但我也找到了this nice utility that converts nibs to objective c代码,尽管我还没有测试过它。

答案 1 :(得分:0)

您是否尝试在dealloc中将出口变量设置为nil? 您正在正确实现setView方法,但是您在viewDidUnload方法中将出口变量设置为nil而不是dealloc。正如here所述,您应该按如下方式实现dealloc:

- (void)setView:(UIView *)aView {
    if (!aView) { // view is being set to nil
        // set outlets to nil, e.g.
        self.anOutlet = nil;
    }
    // Invoke super's implementation last
    [super setView:aView];
}

- (void)dealloc {
    // release outlets and set outlet variables to nil
    [anOutlet release], anOutlet = nil;
    [super dealloc];
}

编辑:如果出口是UIImageViews,那么可能就是你需要做的事情

anOutlet.image = nil;

因为设置UIImage的实例图像属性应该将UIImage实例的保留计数增加1.