UIViewController不会被删除

时间:2014-02-27 15:36:12

标签: ios objective-c memory-management uiviewcontroller

我正在努力解决两个UIViewControllers之间的保留问题。永远不会删除视图控制器,导致我的应用程序内存不断增加内存消耗。

UITitleScreenViewController是我的初始视图控制器。当我从它转到UIChooseAntViewController(选择播放器屏幕)时,我想放弃UITitleViewController的所有权,但正如你在下面的工具中看到的那样,控制器在转换后仍然保留:

snapshot 1

snapshot 2

第二张图片是保留/发布历史记录。 #133之前的所有条目都是在应用启动时发布的。我相信#133和#140是故事板segue创建的对。那么他们的责任是发布额外的释放以摧毁控制器?我尝试在我的willDidDisappear方法上设置self.view = nil但没有交易。

它不仅没有释放控制器,而且每次转换时都会创建它们的新实例。例如,当我从ChooseAnt回到Title时,它会创建另一个UITitleViewController实例!

snapshot 3

重要的事情要说:

1)NSZombies标志未在目标方案中勾选 2)我的UITitleViewController中没有块,我在UIChooseAntController中注释掉了所有块。实际上这些控制器非常简单。 UITitle完全通过故事板定义(只是一个带背景的视图和两个执行segues的按钮)

而UIChooseAnt是一个控件,它显示一个背景和一个滑动界面来显示可用的字符和单选按钮。通过调用[self performSegueWithIdentifier];

以编程方式执行segue

3)我不知道这是否重要,但是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

2 个答案:

答案 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