【问题标题】:iphone app with multiple views/subviews: memory is not being deallocated具有多个视图/子视图的 iphone 应用程序:内存没有被释放
【发布时间】:2010-12-01 18:16:11
【问题描述】:

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

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

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

如何防止这种情况发生?

我的结构如下,下面有几个cmets:

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

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

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

【问题讨论】:

    标签: iphone memory-management uiviewcontroller nib dealloc


    【解决方案1】:

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

    它说:

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

    我在其他地方读到过 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];
    }
    

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

    如果你有 IBOutletsUIImageViews 并且内存是一个问题(我猜总是这样),你可以在 IB 中设计你想要的所有东西,当需要将它们连接到插座时,删除IB中的图像参考并从代码中创建它。 IB 非常适合布局您的应用程序。必须通过代码完成所有这些事情会很糟糕,但我也找到了this nice utility that converts nibs to objective c 代码,虽然我还没有测试过。

    【讨论】:

    【解决方案2】:

    您是否尝试在 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];
    }
    

    编辑:如果outlets是UIImageViews,那么你可能需要这样做

    anOutlet.image = nil;
    

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

    【讨论】:

    • 感谢您的回复。这将如何与“_name” ivars 和“name”属性一起使用? [self.anOutlet release], self.anOutlet = nil 停止并出现“发送到已释放实例的消息”错误和 [self.anOutlet release], _anOutlet = nil 无效(分配方式)
    • ...或者我应该不使用 _name ivars 和 name 属性?
    • [_name 版本],_name = nil;不会中断,但也不会更改分配
    • 我做了一个 NSLog(@"RC: %i", [anOutlet retainCount]);在 dealloc 中,它正在抛出 2。还有谁可以留住这个人?我使用它们的唯一场合是 [self.outlet setAlpha:0.0];在这种情况下,出口是 UIImageView。在 IB 中,插座正在连接到相应的 UIImageView,没有别的。
    • 感谢您的编辑。问题是:图像是插入到 IB 中的,而不是以编程方式插入的。插座只是对图像的引用,因此我可以对其进行动画处理。在 dealloc 我现在正在做: self.outlet.image = nil; [self.outlet 发布]; self.outlet = nil; (这会导致我的第一条评论中的崩溃)和 _outlet.image = nil; [_出口发布]; _outlet = nil;不会崩溃,但不会卸载任何内存。
    猜你喜欢
    • 2011-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多