5

我有一个 iPhone 应用程序,它根据此链接中解释的框架在框架中加载连续视图(基本上是一个使用方法ViewController加载/删除其他视图的主视图displayView)。在我的应用程序中,我使用的是笔尖(示例链接使用编码视图),所以我的每个ViewControllers都有其随附的笔尖。

在 Instruments 中调试显示没有泄漏,但如果我进入/离开一个部分(带有 View.xib 的 ViewController),笔尖仍保留在内存中,因此在一些输入/输出内存开始累积之后。

我知道笔尖没有被卸载,因为一个笔尖几乎是通过编程创建的(IB 中没有东西),而另一个笔尖确实有在 IB 中创建的图像和按钮。首先加载大的,然后加载小的。您会期望在 Instruments 中的分配减少。

我怎样才能防止这种情况?

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

`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];
}

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

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

4

2 回答 2

6

经过一百万次不同的尝试,我终于遇到了这个论坛

它指出:

显然,在 IB 中分配的图像是使用imageNamed. imageNamed以使它们无法加载的方式缓存图像。您可以加载图像,viewDidLoad然后initWithContentsOfFile将它们分配给视图。

我读过的其他地方imageNamed是魔鬼,所以我宁愿不让我的图像以这种方式加载。

(顺便说一句,这是我正在使用的 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];
}

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

所以几乎如果你有IBOutletsfor UIImageViews 并且内存是一个问题(我猜总是这样),你可以在 IB 中设计你想要的所有东西,当需要将它们连接到插座时,删除 IB 中的图像引用并从中创建它代码。IB 非常适合布局您的应用程序。必须通过代码完成所有这些事情会很糟糕,但我还发现了这个不错的实用程序,可以将 nib 转换为目标 c代码,尽管我还没有测试过它。

于 2009-10-01T07:47:16.170 回答
0

您是否尝试在 dealloc 中将您的出口变量设置为 nil?您正确地实现了 setView 方法,但是您在 viewDidUnload 方法中将出口变量设置为 nil 而不是 dealloc。正如这里所讨论的,您应该按如下方式实现 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。

于 2009-09-27T22:05:56.650 回答