12

First off: My project is ARC enabled and I'm using storyboard.

I have a view controller that pushes a segue (modal),

[self performSegueWithIdentifier: @"goInitialSettings" sender: self];

there i'm setting some parameters and store them. When the parameters are stored (true a button tap), the app should return to the original viewcontroller.

This i am doing with this command:

[self.presentingViewController dismissViewControllerAnimated:NO completion:^{}];

I'm noticing that the viewcontroller that i dismiss, never deallocs. How does this come?

I'm adding the code of the 'presented viewcontroller' below:

@interface CenterChoiceController ()
{
    UIView* _titleBackground;
    UILabel* _lblTitle;
    UIButton* _btnGaVerder;
    UIPickerView* _myPickerView;

    NSArray* _centers;
    UILabel* _adresLine;
    UILabel* _cityLine;
    MKPointAnnotation* _point;
    MKMapView* _mapView;
    UIActivityIndicatorView* _indicator;
    UIAlertView* _alert;
    GCenter* _center;
    DataManager* _dm;
}
@end

@implementation CenterChoiceController


-(void)dealloc
{
    NSLog(@"Centerchoice deallocs");

    _titleBackground = nil;
    _lblTitle = nil;
    _btnGaVerder = nil;
    _myPickerView = nil;
    _point = nil;
    _mapView = nil;
    _indicator = nil;
    _alert = nil;
    _centers = nil;
    _adresLine = nil;
    _cityLine = nil;
    _center = nil;
    _dm = nil;
}

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

- (void)viewDidLoad
{
    [super viewDidLoad];

    _dm = [[DataManager alloc]init];
    if([_dm hasConnectivity])
    {
        [_dm fetchCentersForController:self];
    }
    else
    {
        [self pushErrorMessage:NSLocalizedString(@"nointernetconnection", nil)];
    }
    CAGradientLayer *bgLayer = [BackgroundLayer blueGradient];
    bgLayer.frame = self.view.bounds;
    [self.view.layer insertSublayer:bgLayer atIndex:0];

    _titleBackground = [[UIView alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 44)];
    _titleBackground.backgroundColor = [GColor blueColor];
    [self.view addSubview:_titleBackground];

    _lblTitle = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, self.view.frame.size.width - 10, 44)];
    _lblTitle.textAlignment = NSTextAlignmentRight;
    _lblTitle.textColor = [GColor whiteColor];
    _lblTitle.text = NSLocalizedString(@"bioscoopkeuze", nil);
    [self.view addSubview:_lblTitle];


    _btnGaVerder = [[UIButton alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height - 54, self.view.frame.size.width, 54)];
    [_btnGaVerder setTitle:NSLocalizedString(@"gaverder", nil) forState:UIControlStateNormal];
    _btnGaVerder.titleLabel.font = [_btnGaVerder.titleLabel.font fontWithSize:12];
    _btnGaVerder.backgroundColor = [GColor blueColor];
    [_btnGaVerder setTitleColor:[GColor whiteColor] forState:UIControlStateNormal];
    [_btnGaVerder setShowsTouchWhenHighlighted:YES];
    [_btnGaVerder addTarget:self action:@selector(gaVerder) forControlEvents:UIControlEventTouchUpInside];

    _myPickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, 44, self.view.frame.size.width, 200)];



}

-(void)showLoading
{
    NSLog(@"shows loading");
    _indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
    CGPoint cntr = self.view.center;
    _indicator.center = cntr;
    [_indicator startAnimating];
    [self.view addSubview:_indicator];


}


-(void)hideLoading
{
    NSLog(@"hides loading");
    [_indicator removeFromSuperview];
    _indicator = nil;

}

-(void)pushData:(NSArray *)data
{
    [self.view addSubview:_btnGaVerder];
    [self.view addSubview:_myPickerView];
    _centers = data;
    _myPickerView.delegate = self;
    _myPickerView.dataSource = self;


    _dm = [[DataManager alloc]init];
    GSettings* settings = [_dm loadSettings];

    if(settings == nil)
    {
        settings = [[GSettings alloc]init];
        settings.chosenCenter = [_centers objectAtIndex:0];
        settings.loadedCenter = [_centers objectAtIndex:0];
        _center = settings.chosenCenter;
        settings.notificationsEnabled = YES;
        [self changeAddressLines];
    }

    /*if(settings != nil)
     {
     GCenter* loaded = settings.loadedCenter;

     int i = 0;
     BOOL found = NO;

     while(i < [_centers count] && !found)
     {
     GCenter* center = (GCenter*)[_centers objectAtIndex:i];
     if(settings.loadedCenter.iD == center.iD)
     {
     _center = center;
     settings.chosenCenter = center;
     [_dm storeSettings:settings];
     found = YES;
     }

     i++;
     }
     //[self.myPickerView selectRow:i-1 inComponent:0 animated:NO];

     loaded = nil;
     [self changeAddressLines];

     }
     */
}

-(void) pushErrorMessage: (NSString*) errorMessage
{
    _alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"fout", nil) message:errorMessage delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil];
    _alert.delegate = self;
    [_alert show];

}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if(buttonIndex == 0)
    {
        if(self.navigationController != nil)
        {
            [self.navigationController popViewControllerAnimated:YES];
        }
        else
        {
            //[self initializeData];
        }


    }

}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}



-(void)viewWillDisappear:(BOOL)animated
{
    [_dm cancelCenterRequest];
    /*if(self.tabBarController != nil)
     {
     dm = [[DataManager alloc]init];
     settings = [dm loadSettings];

     if([dm hasConnectivity])
     {
     settings.lastUpdated = nil;
     [dm storeSettings:settings];

     }

     if(settings.loadedCenter.centerCode != settings.chosenCenter.centerCode)
     {
     UIStoryboard *mystoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
     SplashScreenController *controller =  [mystoryboard instantiateViewControllerWithIdentifier:@"root"];
     [self presentViewController:controller animated:YES completion:nil];
     }


     dm = nil;
     settings = nil;

     }
     */
}

-(void)gaVerder
{


    _dm = [[DataManager alloc]init];
    GSettings* settings = [_dm loadSettings];

    if(settings == nil)
    {
        settings = [[GSettings alloc]init];
        settings.notificationsEnabled = YES;
    }
    if(_center != nil)
    {
        settings.chosenCenter = _center;
    }
    [_dm storeSettings:settings];
    [_mapView removeFromSuperview];
    _mapView = nil;
    _titleBackground = nil;
    _lblTitle = nil;
    _btnGaVerder = nil;
    _myPickerView = nil;
    _point = nil;
    _indicator = nil;
    _alert = nil;
    _centers = nil;
    _adresLine = nil;
    _cityLine = nil;
    _center = nil;
    _dm = nil;


    [self.presentingViewController dismissViewControllerAnimated:NO completion:^{}];
    //DEZE BLIJFT HELAAS IN HET GEHEUGEN HANGEN... GEEN OPLOSSING GEVONDEN

    //[self.navigationController popViewControllerAnimated:NO];
}

//PICKERVIEWDELEGATE EN DATASOURCE

// returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
{
    return 1;
}

// returns the # of rows in each component..
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component
{
    return [_centers count];
}

- (UILabel *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view
{
    GCenter* center = (GCenter*)[_centers objectAtIndex:row];
    NSString* string = center.name;
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, pickerView.frame.size.width, 44)];
    label.textColor = [GColor blueColor];
    label.font = [label.font fontWithSize:18];
    label.text = string;
    label.textAlignment = NSTextAlignmentCenter;
    return label;

}

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
    _center = (GCenter*)[_centers objectAtIndex:row];

    [self changeAddressLines];

}


-(void)changeAddressLines
{
    if (_mapView != nil)
    {
        [_mapView removeAnnotation:_point];
    }

    [_adresLine removeFromSuperview];
    [_cityLine removeFromSuperview];
    _adresLine = nil;
    _cityLine = nil;



    CGRect rctAdres = CGRectMake(0, _myPickerView.frame.origin.y + _myPickerView.frame.size.height -10, self.view.frame.size.width, 20);
    _adresLine = [[UILabel alloc]initWithFrame:rctAdres];
    _adresLine.textAlignment = NSTextAlignmentCenter;
    _adresLine.textColor = [GColor greyColor];
    _adresLine.text = _center.street;

    CGRect rctCity = CGRectMake(0, rctAdres.origin.y + rctAdres.size.height, self.view.frame.size.width, 20);
    _cityLine = [[UILabel alloc]initWithFrame:rctCity];
    _cityLine.textAlignment = NSTextAlignmentCenter;
    _cityLine.textColor = [GColor greyColor];
    _cityLine.font = [_cityLine.font fontWithSize:14];
    _cityLine.text = _center.city;
    [self.view addSubview:_adresLine];
    [self.view addSubview:_cityLine];

    if(_mapView == nil)
    {

        double height;

        height = _btnGaVerder.frame.origin.y - _cityLine.frame.origin.y - _cityLine.frame.size.height;


        CGRect mapRect = CGRectMake(0, _cityLine.frame.origin.y+3 + _cityLine.frame.size.height, self.view.frame.size.width, height);
        _mapView = [[MKMapView alloc]initWithFrame:mapRect];
        [self.view addSubview:_mapView];
    }

    CLLocationCoordinate2D punt;
    punt.latitude = _center.latitude;
    punt.longitude =  _center.longitude;
    _point = [[MKPointAnnotation alloc] init];
    [_point setCoordinate:punt];
    _mapView.centerCoordinate = punt;
    _point.title = _center.name;
    [_mapView addAnnotation:_point];
    [_mapView setCenterCoordinate:punt animated:YES];
    MKCoordinateRegion theRegion = _mapView.region;
    theRegion.span.longitudeDelta = 0.005;
    theRegion.span.latitudeDelta = 0.005;
    [_mapView setRegion:theRegion animated:YES];



}




@end
Qantas 94 Heavy
  • 14,790
  • 31
  • 61
  • 78
Kornelito Benito
  • 1,003
  • 1
  • 15
  • 32
  • How do you know the memory isn't freed? – duci9y Aug 11 '14 at 14:49
  • in the -(void)dealloc method i wrote a NSLog(@"deallocing view controller"). When calling the viewcontroller, I also notice a growth of memory, when going back, the memory growth stays. – Kornelito Benito Aug 11 '14 at 14:53
  • 1
    Do you maintain any strong references to the view controller you present? – duci9y Aug 11 '14 at 14:56
  • I guess not: interface CenterChoiceController () { UIView* _titleBackground; UILabel* _lblTitle; UIButton* _btnGaVerder; UIPickerView* _myPickerView; NSArray* _centers; UILabel* _adresLine; UILabel* _cityLine; MKPointAnnotation* _point; MKMapView* _mapView; UIActivityIndicatorView* _indicator; UIAlertView* _alert; GCenter* _center; DataManager* _dm; } @end – Kornelito Benito Aug 11 '14 at 14:59
  • Something you're doing in the presented view controller is preventing it from being deallocated -- you need to show the code you're using in that controller. – rdelmar Aug 11 '14 at 15:12

2 Answers2

30

In my case it was a little more complicated. I don't have any variable that has strong reference to my view controller, and my view controller is not a strong delegate to any property/variable contained inside this class itself. After some hard thinking and trials, I found my issue was caused by a NSTimer object defined in the interface. The timer object itself is non-repeatable, but the method invoked by it will schedule the timer again at the end, which as you can imagine would reference this method defined in my view controller again, thus causing circular references. To break out of this loop, I had to invalidate the timer before I dismiss my view controller.

As a summary, these are cases when a view controller can be blocked from deallocating after it is dismissed:

  1. The view controller is being strongly referenced by some outside object;
  2. The view controller is a strong delegate referenced by some object defined within the view controller itself
  3. The dismissViewControllerAnimated:completion: block may reference to self or it has some other code block that may cause a circular references
  4. The view controller has NSTimer objects which can invoke some methods which re-schedules the timer

There could be more, but hopefully we can capture a lot of cases with the above cases.

CodeBrew
  • 5,295
  • 1
  • 34
  • 43
8

If your view controller is not deallocated after it is dismissed, there's probably a strong reference to that view controller somewhere in your code. ARC will always deallocate objects that doesn't have strong reference anymore.

Enrico Susatyo
  • 18,061
  • 17
  • 89
  • 153