我正在努力解决两个UIViewControllers之间的保留问题。永远不会删除视图控制器,导致我的应用程序内存不断增加内存消耗。
UITitleScreenViewController是我的初始视图控制器。当我从它转到UIChooseAntViewController(选择播放器屏幕)时,我想放弃UITitleViewController的所有权,但正如你在下面的工具中看到的那样,控制器在转换后仍然保留:
第二张图片是保留/发布历史记录。 #133之前的所有条目都是在应用启动时发布的。我相信#133和#140是故事板segue创建的对。那么他们的责任是发布额外的释放以摧毁控制器?我尝试在我的willDidDisappear方法上设置self.view = nil但没有交易。
它不仅没有释放控制器,而且每次转换时都会创建它们的新实例。例如,当我从ChooseAnt回到Title时,它会创建另一个UITitleViewController实例!
重要的事情要说:
1)NSZombies标志未在目标方案中勾选 2)我的UITitleViewController中没有块,我在UIChooseAntController中注释掉了所有块。实际上这些控制器非常简单。 UITitle完全通过故事板定义(只是一个带背景的视图和两个执行segues的按钮)
而UIChooseAnt是一个控件,它显示一个背景和一个滑动界面来显示可用的字符和单选按钮。通过调用[self performSegueWithIdentifier];
以编程方式执行segue3)我不知道这是否重要,但是segue被定义为模态并且没有动画。
编辑:4)没有控制器相互引用。
以下是TitleViewController的源代码
这个问题让我抓狂。如果有人能说清楚它。什么都会有很大的帮助!谢谢!
@interface SMTitleScreenViewController ()
@property (weak, nonatomic) IBOutlet UIButton *buttonPlay;
@property (weak, nonatomic) IBOutlet UIButton *buttonCamera;
- (IBAction)onButtonPlay:(id)sender;
- (IBAction)onButtonCamera:(id)sender;
@end
@implementation SMTitleScreenViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIColor* color = [UIColor colorWithRed:0.2509f green:0.1176f blue:0.0745f alpha:1.0f];
UIFont* font = [UIFont fontWithName:@"Jungle Roar" size:BUTTON_FONT_SIZE];
NSString* playString = NSLocalizedString(@"Play", @"");
NSString* cameraString = NSLocalizedString(@"Camera", @"");
[self.buttonPlay setTitle:playString forState:UIControlStateNormal];
[self.buttonPlay setTitle:playString forState:UIControlStateHighlighted];
[self.buttonPlay setTitleColor:color forState:UIControlStateNormal];
[self.buttonPlay setTitleColor:color forState:UIControlStateHighlighted];
self.buttonPlay.titleLabel.font = font;
[self.buttonCamera setTitle:cameraString forState:UIControlStateNormal];
[self.buttonCamera setTitle:cameraString forState:UIControlStateHighlighted];
[self.buttonCamera setTitleColor:color forState:UIControlStateNormal];
[self.buttonCamera setTitleColor:color forState:UIControlStateHighlighted];
self.buttonCamera.titleLabel.font = font;
}
- (void) viewDidDisappear:(BOOL)animated
{
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (IBAction)onButtonPlay:(id)sender
{
}
- (IBAction)onButtonCamera:(id)sender
{
}
编辑:UIChooseAntViewController(根据要求)
@interface SMChooseAntViewController ()
@property (strong, nonatomic) UIImageView* rope;
@property (strong, nonatomic) UIImageView* antFrontLayer;
@property (strong, nonatomic) UIImageView* antBackLayer;
@property (strong, nonatomic) NSArray* antFrontImages;
@property (strong, nonatomic) NSArray* antBackImages;
@property (strong, nonatomic) NSArray* antNameImages;
@property (strong, nonatomic) UIButton* leftButton;
@property (strong, nonatomic) UIButton* rightButton;
@property (strong, nonatomic) UIButton* confirmButton;
@property (nonatomic) NSUInteger selectedAntID;
@property (strong, nonatomic) UIImage* radioImageHighlighted;
@property (strong, nonatomic) UIImage* radioImage;
@property (strong, nonatomic) NSMutableArray* radioViews;
@property (weak, nonatomic) IBOutlet UILabel *antDescriptionLabel;
@property (weak, nonatomic) IBOutlet UIImageView *antDescriptionBG;
@property (strong, nonatomic) UIImageView* antNameView;
@property (strong, nonatomic) UISwipeGestureRecognizer* leftSwipeRecognizer;
@property (strong, nonatomic) UISwipeGestureRecognizer* rightSwipeRecognizer;
- (void) onArrowButton:(id)sender;
- (void) onConfirmButton:(id)sender;
- (void) respondToSwipe:(UISwipeGestureRecognizer*)recognizer;
@end
@implementation SMChooseAntViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
{
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
// Needed to come in between front and back player image layers
UIImage* ropeImage = [UIImage imageNamed:ROPE_IMAGE_PATH];
self.rope = [[UIImageView alloc] initWithImage:ropeImage];
self.rope.center = CGPointMake(screenSize.width / 2.0f, ropeImage.size.height / 2.0f);
UIColor* brownColor = [UIColor colorWithRed:0.2509f green:0.1176f blue:0.0745f alpha:1.0f];
self.antDescriptionLabel.textColor = brownColor;
self.antDescriptionLabel.numberOfLines = 0;
NSArray* antNames = [SMProfile antNames];
// Cache available Player Views in a NSArray
UIImage* frontImages[MAX_AVAILABLE_ANTS];
UIImage* backImages[MAX_AVAILABLE_ANTS];
UIImage* nameImages[MAX_AVAILABLE_ANTS];
for (NSUInteger i = 0; i < MAX_AVAILABLE_ANTS; ++i)
{
NSString* antName = [antNames objectAtIndex:i];
frontImages[i] = [SMImage imageNamed:[NSString stringWithFormat:@"%@_title_front.png", antName]];
backImages[i] = [SMImage imageNamed:[NSString stringWithFormat:@"%@_title_back.png", antName]];
nameImages[i] = [SMImage imageNamed:[NSString stringWithFormat:@"%@_name.png", antName]];
}
self.antFrontImages = [NSArray arrayWithObjects:frontImages[0], frontImages[1], frontImages[2], nil];
self.antBackImages = [NSArray arrayWithObjects:backImages[0], backImages[1], backImages[2], nil];
self.antNameImages = [NSArray arrayWithObjects:nameImages[0], nameImages[1], nameImages[2], nil];
// Load Selected player from profile
SMProfile* profile = [SMProfile mainProfile];
self.selectedAntID = profile.antID.unsignedIntegerValue;
self.antFrontLayer = [[UIImageView alloc] initWithImage:[self.antFrontImages objectAtIndex:self.selectedAntID]];
self.antBackLayer = [[UIImageView alloc] initWithImage:[self.antBackImages objectAtIndex:self.selectedAntID]];
self.antNameView = [[UIImageView alloc] initWithImage:[self.antNameImages objectAtIndex:self.selectedAntID]];
self.antNameView.center = CGPointMake(screenSize.width / 2.0f, self.antDescriptionBG.frame.origin.y);
NSString* antDescriptionKey = [NSString stringWithFormat:@"AntDescription%lu", (unsigned long)self.selectedAntID];
self.antDescriptionLabel.text = NSLocalizedString(antDescriptionKey, @"");
self.antDescriptionLabel.numberOfLines = 0;
self.antDescriptionLabel.adjustsFontSizeToFitWidth = YES;
self.antFrontLayer.center = CGPointMake(screenSize.width / 2.0f, ropeImage.size.height * 0.75f);
self.antBackLayer.center = self.antFrontLayer.center;
// Here a perform button creation, loading and positioning
// No blocks are being called
// add Target to buttons
[self.leftButton addTarget:self action:@selector(onArrowButton:) forControlEvents:UIControlEventTouchUpInside];
[self.rightButton addTarget:self action:@selector(onArrowButton:) forControlEvents:UIControlEventTouchUpInside];
[self.confirmButton addTarget:self action:@selector(onConfirmButton:) forControlEvents:UIControlEventTouchUpInside];
// Create and configure SwipeRecognizers
self.leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(respondToSwipe:)];
self.leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:self.leftSwipeRecognizer];
self.rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(respondToSwipe:)];
self.rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:self.rightSwipeRecognizer];
// Here a create a custom page control scheme. I load two radio button images
// create views and add them to the root view node.
// Add remaining view to the hierarchy
[self.view addSubview:self.antBackLayer];
[self.view addSubview:self.rope];
[self.view addSubview:self.antFrontLayer];
[self.view addSubview:self.confirmButton];
[self.view bringSubviewToFront:self.antDescriptionBG];
[self.view bringSubviewToFront:self.antDescriptionLabel];
[self.view addSubview:self.leftButton];
[self.view addSubview:self.rightButton];
[self.view addSubview:self.antNameView];
[self.view bringSubviewToFront:[self.radioViews objectAtIndex:0]];
}
- (void) viewDidDisappear:(BOOL)animated
{
if ([self.view window] == nil)
{
self.rope = nil;
self.antFrontLayer = nil;
self.antBackLayer = nil;
self.antFrontImages = nil;
self.antBackImages = nil;
self.antNameImages = nil;
self.leftButton = nil;
self.rightButton = nil;
self.confirmButton = nil;
self.radioImageHighlighted = nil;
self.radioImage = nil;
self.radioViews = nil;
self.antNameView = nil;
self.leftSwipeRecognizer = nil;
self.rightSwipeRecognizer = nil;
self.view = nil;
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self.view window] == nil)
{
self.view = nil;
}
}
- (void)onArrowButton:(id)sender
{
UIButton* button = (UIButton*)sender;
NSInteger direction = button.tag;
// if on boundaries do nothing (first ant selected and swipe left or last ant selected and swipe right)
if ((self.selectedAntID == 0 && direction == -1) || (self.selectedAntID == (MAX_AVAILABLE_ANTS - 1) && direction == 1))
{
return;
}
// Update Radio Buttons. Unselect previous and select next.
UIImageView* currRadio = [self.radioViews objectAtIndex:self.selectedAntID];
currRadio.image = self.radioImage;
self.selectedAntID = (self.selectedAntID + MAX_AVAILABLE_ANTS + direction) % MAX_AVAILABLE_ANTS;
UIImageView* nextRadio = [self.radioViews objectAtIndex:self.selectedAntID];
nextRadio.image = self.radioImageHighlighted;
self.antFrontLayer.image = [self.antFrontImages objectAtIndex:self.selectedAntID];
self.antBackLayer.image = [self.antBackImages objectAtIndex:self.selectedAntID];
self.antNameView.image = [self.antNameImages objectAtIndex:self.selectedAntID];
// here I was issuing some block to perform the swipe animation for the ant image views. I commented them and I'm just replacing the images now (3 lines above)
}
- (void)onConfirmButton:(id)sender
{
// Save player choice to profile and perform segue
SMProfile* profile = [SMProfile mainProfile];
profile.antID = [NSNumber numberWithUnsignedInt:self.selectedAntID];
[profile save];
[self performSegueWithIdentifier:@"chooseAntToStageSelect" sender:self];
}
- (void) respondToSwipe:(UISwipeGestureRecognizer *)recognizer
{
// forward swipe to onArrowButton message
if (recognizer.direction == UISwipeGestureRecognizerDirectionLeft)
{
[self onArrowButton:self.rightButton];
}
else if (recognizer.direction == UISwipeGestureRecognizerDirectionRight)
{
[self onArrowButton:self.leftButton];
}
}
@end
答案 0 :(得分:0)
当从A呈现B视图控制器时,A将不会释放,因为A是presentingViewController
(请参阅sdk doc)。
或者,如果A,B是导航控制器的子视图控制器,则A是存储在推送堆栈中,当推送到B时不会被移除。
答案 1 :(得分:0)
您正在将视图控制器推送到堆栈,因此直到最后一个未弹出,控制器才会被释放。
要深入了解对孩子的依赖,请阅读以下文章。
很好地解释,我相信它会有所帮助。 :)
http://www.cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html