0

更新:
澄清问题的背景;问题不在于如何避免该问题,而是要澄清该文档的含义,因为我的实验表明我期望根据文档卸载的视图并未卸载。我想了解这是否是一个错误,或者我是否误解了文档。为了解决这个问题,我知道在 viewWillAppear 而不是 viewDidLoad 中设置图像,并在 viewDidDisappear 中将图像设置为 nil,正在释放内存并且应用程序不会崩溃。但是,我想了解是否应该使用初始代码释放内存,因为实验是模拟将此类视图控制器用作插座,并在 Interface Builder 中设置 UI 图像(背景...),而不是设置它们在 viewWillAppear 中的代码中......

原文:
我正在尝试了解 iOS6 的一些新方面,如 View Controller Programming Guide 中所述:

在 iOS 6 及更高版本中,视图控制器在需要时卸载自己的视图视图控制器的默认行为是在第一次访问视图属性时加载其视图层次结构,然后将其保存在内存中,直到视图控制器被释放。视图在屏幕上绘制自身所使用的内存可能非常大。但是,当视图未附加到窗口时,系统会自动释放这些昂贵的资源。大多数视图使用的剩余内存足够小,以至于系统不值得自动清除和重新创建视图层次结构。

假设我正在创建一个带有 rootViewController 的简单应用程序。这个 rootViewController 有几个子视图控制器,都声明为 IBOutlets,而不是在代码中分配。

@property(nonatomic,strong) IBOutlet ChildViewController *childViewController1;
@property(nonatomic,strong) IBOutlet ChildViewController *childViewController2;
@property(nonatomic,strong) IBOutlet ChildViewController *childViewController3;
@property(nonatomic,strong) IBOutlet ChildViewController *childViewController4;
@property(nonatomic,strong) IBOutlet ChildViewController *childViewController6;
@property(nonatomic,strong) IBOutlet ChildViewController *childViewController7;
@property(nonatomic,strong) IBOutlet ChildViewController *childViewController8;
@property(nonatomic,strong) IBOutlet ChildViewController *childViewController9;
@property(nonatomic,strong) IBOutlet ChildViewController *childViewController10;

rootViewController 有几个按钮,按下每个按钮都会对每个 childViewController 执行简单的 presentModalViewController 操作

-(IBAction)showChild1Action:(id)sender{
  [self presentModalViewController:self.childViewController1 animated:true];
}


每个 childViewController 都有一个关闭按钮,用于关闭子视图控制器。

-(IBAction)closeAction:(id)sender{
  [self dismissModalViewControllerAnimated:true];
}


My expectation from the documentation was that the child view controller view objects would be released from memory, as the child view controllers are dismissed.
However I purposely tested with large view objects, and running profile on such an app, the memory usage just keeps growing as each child controller gets presented, and the app eventually crashes after I present child controller #7 or so.

What is your understanding of what has changed in iOS6 in that aspect?

RootViewController.h

#import <UIKit/UIKit.h>
#import "ChildViewController.h"

@interface RootViewController : UIViewController

@property(nonatomic,strong) IBOutlet ChildViewController *level2ViewController1;
@property(nonatomic,strong) IBOutlet ChildViewController *level2ViewController2;
@property(nonatomic,strong) IBOutlet ChildViewController *level2ViewController3;
@property(nonatomic,strong) IBOutlet ChildViewController *level2ViewController4;
@property(nonatomic,strong) IBOutlet ChildViewController *level2ViewController5;
@property(nonatomic,strong) IBOutlet ChildViewController *level2ViewController6;
@property(nonatomic,strong) IBOutlet ChildViewController *level2ViewController7;
@property(nonatomic,strong) IBOutlet ChildViewController *level2ViewController8;
@property(nonatomic,strong) IBOutlet ChildViewController *level2ViewController9;
@property(nonatomic,strong) IBOutlet ChildViewController *level2ViewController10;

-(IBAction)showChild1Action:(id)sender;
-(IBAction)showChild2Action:(id)sender;
-(IBAction)showChild3Action:(id)sender;
-(IBAction)showChild4Action:(id)sender;
-(IBAction)showChild5Action:(id)sender;
-(IBAction)showChild6Action:(id)sender;
-(IBAction)showChild7Action:(id)sender;
-(IBAction)showChild8Action:(id)sender;
-(IBAction)showChild9Action:(id)sender;
-(IBAction)showChild10Action:(id)sender;

@end

RootViewController.m

#import "RootViewController.h"

@interface RootViewController ()

@end

@implementation RootViewController

@synthesize level2ViewController1;
@synthesize level2ViewController2;
@synthesize level2ViewController3;
@synthesize level2ViewController4;
@synthesize level2ViewController5;
@synthesize level2ViewController6;
@synthesize level2ViewController7;
@synthesize level2ViewController8;
@synthesize level2ViewController9;
@synthesize level2ViewController10;


- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}

-(IBAction)showChild1Action:(id)sender{
    self.level2ViewController1.index=0;
    [self presentModalViewController:self.level2ViewController1 animated:true];
}
-(IBAction)showChild2Action:(id)sender{
    self.level2ViewController2.index=1;
    [self presentModalViewController:self.level2ViewController2 animated:true];
}
-(IBAction)showChild3Action:(id)sender{
    self.level2ViewController3.index=2;
    [self presentModalViewController:self.level2ViewController3 animated:true];
}
-(IBAction)showChild4Action:(id)sender{
    self.level2ViewController4.index=3;
    [self presentModalViewController:self.level2ViewController4 animated:true];
}
-(IBAction)showChild5Action:(id)sender{
    self.level2ViewController5.index=4;
    [self presentModalViewController:self.level2ViewController5 animated:true];
}
-(IBAction)showChild6Action:(id)sender{
    self.level2ViewController6.index=5;
    [self presentModalViewController:self.level2ViewController6 animated:true];
}
-(IBAction)showChild7Action:(id)sender{
    self.level2ViewController7.index=6;
    [self presentModalViewController:self.level2ViewController7 animated:true];
}
-(IBAction)showChild8Action:(id)sender{
    self.level2ViewController8.index=7;
    [self presentModalViewController:self.level2ViewController8 animated:true];
}
-(IBAction)showChild9Action:(id)sender{
    self.level2ViewController9.index=8;
    [self presentModalViewController:self.level2ViewController9 animated:true];
}
-(IBAction)showChild10Action:(id)sender{
    self.level2ViewController10.index=9;
    [self presentModalViewController:self.level2ViewController10 animated:true];
}

@end

ChildViewController.h

#import <UIKit/UIKit.h>

@interface ChildViewController : UIViewController

@property(nonatomic,weak) IBOutlet UIImageView *image1;

@property NSInteger index;

-(IBAction)closeAction:(id)sender;

@end

ChildViewController.m

#import "ChildViewController.h"

@interface ChildViewController ()

@end

@implementation ChildViewController

@synthesize image1;
@synthesize index;


- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self) {
        // Custom initialization
    }
    return self;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    if(self.index==0)
        [self.image1 setImage:[UIImage imageNamed:@"IMG1.JPG"]];
    if(self.index==1)
        [self.image1 setImage:[UIImage imageNamed:@"IMG2.JPG"]];
    if(self.index==2)
        [self.image1 setImage:[UIImage imageNamed:@"IMG3.JPG"]];
    if(self.index==3)
        [self.image1 setImage:[UIImage imageNamed:@"IMG4.JPG"]];
    if(self.index==4)
        [self.image1 setImage:[UIImage imageNamed:@"IMG5.JPG"]];
    if(self.index==5)
        [self.image1 setImage:[UIImage imageNamed:@"IMG6.JPG"]];
    if(self.index==6)
        [self.image1 setImage:[UIImage imageNamed:@"IMG7.JPG"]];
    if(self.index==7)
        [self.image1 setImage:[UIImage imageNamed:@"IMG8.JPG"]];
    if(self.index==8)
        [self.image1 setImage:[UIImage imageNamed:@"IMG9.JPG"]];
    if(self.index==9)
        [self.image1 setImage:[UIImage imageNamed:@"IMG10.JPG"]];

}

- (void)viewDidUnload
{
    [super viewDidUnload];
    // Release any retained subviews of the main view.
    // e.g. self.myOutlet = nil;
}

-(IBAction)closeAction:(id)sender{
    [self dismissModalViewControllerAnimated:true];
}

When using Instruments I get these observations:

  • The memory allocation instrument is showing what I would expect to see: total allocation that goes up when child view controller is presented, goes down when child view is dismissed This is after showing then dismissing 3 child view controllers

  • However the activity indicator is telling a different story, with real memory increasing with every presentModalViewController, and never decreasing when dismissing them

This is also after showing/dismissing 3 child view controllers

4

3 回答 3

1

The answer I eventually got back from Apple:

Your "large resources" happen to be images that you've loaded cached via +imageNamed:. Because they are loaded cached, they are exempted from the automatic cleanup. Generally only content generated via -drawRect: or by Core Animation is automatically released here. Because your views continue to exist, they continue to hold a reference to these cached images, and we can't purge them on memory warning either

Looks like cached resource isn't part of the automatic cleanup mentioned in the doc, and those resources only get deallocated when they stop being referenced.

于 2013-02-01T00:26:09.630 回答
0

Sounds like you have a leak somewhere. Even if the system isn't releasing those views, memory use should max out after you've presented all 10 child controllers. If memory use grows without bound, your child controllers are probably giving up their views (and so creating new ones each time they're presented), but the views aren't being deallocated -- classic symptom of an over-retain situation. Try cutting the number of child controllers down to two and see if the same thing eventually happens. Or, use Instruments to look for a leak.

Update: -[UIImage imageNamed] has a reputation for never releasing the images it loads -- that may be the reason for your memory growth. Try loading the images using a different method, or not at all (since they're not really important to the experiment).

于 2012-10-02T23:13:06.493 回答
0

Instead of using imageNamed: use initWithContentsOfFile.

For example:

__weak NSString *filePath = [[NSBundle mainBundle] pathForResource:@"image" ofType:@"png"];
self.imageView.image = [[UIImage alloc] initWithContentsOfFile:filePath];

Because alloc method is being used, GC is marking this object. You can even mark the filePath string for GC cleanup.

于 2013-05-03T14:50:54.003 回答