简体   繁体   中英

Memory management ARC and view controllers

I was thinking about this today, and now i've tested i'm a little confused…

When using viewControllers either by pushing a viewController onto the Navigation Stack or Presenting a ViewController modally I'm wondering about memory management.

Lets use the modal example as a thought experiment, here is the source to create and present the view, in my example it doesn't matter if ARC or not so here's both:

With ARC:

ViewController *myViewController = [[ViewController alloc] init];  
myViewController.delegate = self;
[self presentViewController:myViewController animated:YES completion:NULL];

Without ARC:

ViewController *myViewController = [[ViewController alloc] init];  
myViewController.delegate = self;
[self presentViewController:myViewController animated:YES completion:NULL];
[myViewController release];                   //As it's now 'owned' by the presenting View controller

This would be my understanding about how to present a viewController modally over an existing ViewController.

Lets say for our example the above code resides in a method which is called when a button is touched to present the ViewController.

Now to my question,

What I am doing is calling this code each time a button is touched, During testing with Instruments I didn't seem to have any leaks. - However because I have NSLog statements in the myViewController dealloc & viewDidLoad methods I know that it's getting instanciated everytime I touch the button but never deallocated.

So...

A) Why am I not getting a leak showing ( or a rise in Live Bytes ) in instruments (when either using ARC or not) because I am seemingly creating a new viewController and leaking the old one each time I go to present it.

B) What is the correct way to write the above code if this is not memory safe? I see this kind of code snippets all over Apple's example code and internet. Should I (and they) not be wrapping the alloc init line in an if statement to check if the object is already created?

ie

if(!myViewController)
{
    ViewController *myViewController = [[ViewController alloc] init];  
}
myViewController.delegate = self;
[self presentViewController:myViewController animated:YES completion:NULL];

Thanks for taking the time to read and answer, I really wonder about this as I've been creating, pushing and presenting ViewControllers using the above code the whole time, and never noticed a leak! - might have to go back and rewrite it all!

To avoid confusion please note: The delegate property is a custom property of my UIViewController subclass (where I've implemented a delegate protocol), required to dismiss the Modally present Viewcontroller properly. As per coding guidelines.

Regards, John

EDIT As Requested, Creation of the delegate:

.h

@protocol NotificationManagementViewControllerDelegate;

@interface NotificationManagementController : 
{
    __weak NSObject <NotificationManagementViewControllerDelegate> *delegate;
}
@property (nonatomic, weak) NSObject <NotificationManagementViewControllerDelegate> *delegate;
@protocol NotificationManagementViewControllerDelegate <NSObject>

@optional
- (void)didFinishSettingNotification:(NotificationManagementController *)notificationManagementController;

.m

- (void)sendMessageToDismiss {
    if ([[self delegate] respondsToSelector:@selector(didFinishSettingNotification:)]) {
        [self.delegate didFinishSettingNotification:self];
    }
}

And finally in the delegates .m:

- (void)didFinishSettingNotification:(NotificationManagementController *)notificationManagementController
{
    [self dismissViewControllerAnimated:YES completion:NULL];
}

You are not getting a leak because you create a new controller and ARC will release this allocation for you.

But, it's better to create a @property for your new view controller. and modify your ie implementation like :

@property (nonatomic, strong) ViewController *myViewController;

if (!_myViewController)
    self.myViewController = [[ViewController alloc] init];  

self.myViewController.delegate = self;
[self presentViewController:_myViewController animated:YES completion:nil];

Here, you have a lazy property and you don't create a new one ViewController after the first creation. But, you need to pass your delegate (or any property) outside your test.

Furthermore, if you use your first implementation and add this controller in a subview of the current controller without property, this will work but you will get a leak. I got this experience with the code below :

RootViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    ViewController *myViewController = [[ViewController alloc] init]; 
    [self.view addSubview:myViewController.view];
}

myViewController will be add on the screen but released immediately without keeping any reference of the object, so if you add an action in 'ViewController`, your application will crash without explanation of XCode.

So, the correct way to write this without leak will be :

- (void)viewDidLoad
{
    [super viewDidLoad];

    if (!_myViewController)
        self.myViewController = [[ViewController alloc] init];

    [self.view addSubview:self.myViewController.view];
}

The answer is a bit longer and can be improved so don't hesitate ! Hope it's going to help some people.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM