iosobjective-cdelegatesautomatic-ref-countingstrong-references

Does this objective-c code create a strong reference cycle?


I am working in someone else's iOS project. ARC is (and to the best of my knowledge always has been) enabled. The code is littered with button-press handlers that create an instance of some view controller, set its delegate to self, and then show it, e.g.,

- (IBAction)keyButton:(id)sender {
    MyViewController *controller = [[MyViewController alloc] init];
    controller.delegate = self;
    [self.navigationController pushViewController:controller animated:YES];
}

MyViewController shows a spin widget for choosing a musical key. Once chosen, the user presses back to use the selected key to make music. Those are the only user interactions, and there are no background processes etc.

In the MyViewController.h file, the delegate is declared as follows:

@interface MyViewController : UIViewController {
    id <ModuleUpdateDelegate> _delegate;
}

@property (nonatomic,strong) id delegate;

I have two questions:

  1. Why might the original author have chosen to create a new instance of MyViewController with every button press?
  2. Who owns each such instance? [When] does it get destroyed? Since it holds a strong reference to its delegate, is the current implementation creating a strong reference cycle [with each button press]?

My own instinct is to create a private, MyViewController * property in the delegate, which gets loaded on the first button press only. Then I would make the delegate property in MyViewController weak instead of strong, per the general recommendation. But like I said, the current implementation is quite ubiquitous, so I am leery of changing anything without first understanding how it works.

Any help is appreciated.


Solution

  • In this case, it doesn't matter who MyViewController retains, it matters who retains the instance of MyViewController.

    In the code you posted, the strong reference to MyViewController is held by self.navigationController. As soon as MyViewController is popped, then its retain count will hit 0 and it will be released. As long as controller.delegate doesn't retain controller, there is no cycle.

    That said, you must be very careful with strong back references (eg. things like delegates). When in doubt use Instruments to check for retain cycles or memory leaks.


    My advice: be cautions. Turn the strong delegate into a weak delegate if possible. Even if there is no retain cycle right now, you could easily create one without realizing it.