4

I'm just starting iOS programming and am playing around with trying to switch view controllers programatically (i.e. go from one view to another). I know that the view controller that presents the next view controller needs to be released after the other one is presented but I can't seem to get anything that works. I have tried dismissing the controller after presenting the next controller but I still get a memory leak.

So I have this code in ViewControllerA:

- (void) switchViews {
    [self presentViewController:[[ViewControllerB alloc] init] animated:NO completion:nil];
}

and this in ViewControllerB:

- (void) switchViews {
    [self presentViewController:[[ViewControllerA alloc] init] animated:NO completion:nil];
}

Buttons in the views fire these events and basically they just switch from one view to the other.

So how do I switch views back and forth so that a memory leak isn't created? And as a side note I am using ARC.

Cezar
  • 55,636
  • 19
  • 86
  • 87
Chris Kdon
  • 916
  • 1
  • 14
  • 26
  • 1
    This isn't an answer, but in objective-c the `BOOL` type expects either `YES` or `NO` rather than `true` or `false`. – danielbeard Aug 11 '13 at 18:28
  • 2
    it also doesn't look like you are dismissing the old VCs, just continuously presenting them. Do you dismiss them somewhere? – CaptJak Aug 11 '13 at 18:29
  • 1
    There is leak because every time you call the method 'switchViews', you allocate memory and create a new viewcontrollerA/B, instead of checking whether such controllers already exist in the memory and reuse them. – Jing Aug 11 '13 at 18:31
  • @danielbeard ahh thanks for letting me know the BOOL thing, I just started Objective-C. I've updated the question. – Chris Kdon Aug 11 '13 at 18:34
  • And @CaptJak that's my issue although I'm using Automatic Reference Counting they just keep calling each other. Thus presumably holding references to each new instance. I need a way to dismiss the old controller after the new one appears. And `dismissViewControllerAnimated:completion:` doesn't seem to be helping. The profiler just shows memory constantly increasing on each switch. – Chris Kdon Aug 11 '13 at 18:35
  • 1
    Well, since B comes from A, instead of using `presentViewController` to go back to A, just use `[self dismissViewControllerAnimated:NO completion:nil];` in VC B to go back to A. That way you don't keep recreating in an endless loop. – CaptJak Aug 11 '13 at 18:40
  • @CaptJak that worked! For some reason I didn't think `dismissViewControllerAnimated` would take me back. I just thought it would remove it from memory which is why I was confused it wasn't working. I'll need to read the docs more. Thanks. If you want to make that comment an answer I'll mark it as such. – Chris Kdon Aug 11 '13 at 18:47

2 Answers2

3

Basically what you are doing in your code above is creating ViewController A, then ViewControllerB, then another ViewController A, and then another ViewController B and then another ViewController A, and then another ViewController B.

What you should be doing is start with ViewController A, create ViewController B, and then to go back to ViewController A, just dismiss Viewcontroller B.

Read this blog for advice on how to do it the simple way that I've explained.

Now, there is a chance that using this method can cause some problems, and they will most likely only happen when you want to do a little more than just dismiss the current ViewController. Apple does, in fact state in their docs:

"When it comes time to dismiss a presented view controller, the preferred approach is to let the presenting view controller dismiss it. In other words, whenever possible, the same view controller that presented the view controller should also take responsibility for dismissing it. Although there are several techniques for notifying the presenting view controller that its presented view controller should be dismissed, the preferred technique is delegation."

Now, as the answer from @LeoNatan has said, it can be bad practice to do this by using [self dismissViewControllerAnimated:completion:. And the above paragraph is why, I believe, he said that it was a bad idea.

It is, indeed good practice to learn how to do it both ways. And although my answer is the quick way, it is also a little dirtier.

CaptJak
  • 3,592
  • 1
  • 29
  • 50
  • Thanks for the blog post. Great info. – Chris Kdon Aug 11 '13 at 19:01
  • @CaptJak What will happen when there are multiple view controllers like A,B,C,D,E,F. So the presenting flow moves as from A, B has been presented then C. Then from F we need to display A !! What is the best practice for that ??? – Karan Alangat Mar 31 '16 at 08:33
  • 1
    @KaranAlangat, To jump back to several VCs, you can use the unwind segue. I had that same question, which I posted [here](https://stackoverflow.com/questions/17606493/dismissing-both-uinavigation-views-and-modal-views-at-once-programmatically). See the answer. It's Objective-C, but it's still valid. – CaptJak Mar 31 '16 at 13:09
  • @CaptJak I am not using segues as the screens reside in different storyboards ??? – Karan Alangat Apr 04 '16 at 10:58
  • @KaranAlangat: Make sure you use a navigation controller, then read through [this part of the Apple Docs](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UINavigationController_Class/) to find what will work. If you can't figure it out, then search on SO for the answer, or post another question. The one we are commenting on is almost 3 years old. – CaptJak Apr 04 '16 at 13:26
  • @CaptJak pls check this question http://stackoverflow.com/questions/36330047/will-multiple-presentviewcontroller-method-calls-throughout-the-view-controllers – Karan Alangat Apr 04 '16 at 13:32
1

This should be a good exercise for you, studying Objective C and design patterns.

Create a ViewControllerBDelegate protocol with a "viewControllerBDidClose:" method. Create a property in ViewControllerB called "delegate". Now, after creating vcB in vcA, set the delegate object to be vcA. Now, when you want to go back to vcA from vcB, you should notify the delegate that you wish to close vcB. vcA would then "dismissViewController:animated:" vcB.

This design pattern is called delegation and presents a two-way "conversation" between an object and its delegate. The delegate "converses" with the object by using the public API, while the object "converses" with its delegate using the delegation protocol.

In the comments, someone recommended that you dismiss vcB from itself. This is a bad practice, and is never recommended. Whoever presented a view controller should be the one to dismiss it. By way of delegation, you notify the presenter that the presented is finished and that the presenter should dismiss the presented.

Léo Natan
  • 56,823
  • 9
  • 150
  • 195