Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why there is no retain loop between UINavigationController and UIViewControllers

Situation: there is UINavigationController with pushed UIViewController.

1.UIViewController has strong reference to UINavigationController

@property(nonatomic,readonly,retain) UINavigationController *navigationController

2.UINavigationController stores view controllers in NSArray

@property(nonatomic,copy) NSArray *viewControllers;

UINavigationController should have strong reference to this NSArray (or it will be deallocated).

3.NSArray has strong references to contained view controllers.

UPDATE: Lets imagine somewhere in code we have following:

UIViewController *A = [ [UIViewController alloc] init ];
UINavigationController *B = [ [ UINavigationController alloc ] initWithRootViewController:A ];
// Here we have strong reference in A to B, in B to B.viewControllers (count == 1) and in B.viewControllers to A.
// Local variable's strong references to A and B
A = nil; B = nil;
// Local variable's strong references has gone
// But we should still have retain loop here
// Magic !?? :)

My question is why we do not have retain loop here?

like image 886
Avt Avatar asked Feb 22 '14 20:02

Avt


People also ask

What is uinavigationcontroller UIViewController?

@MainActor class UINavigationController : UIViewController A navigation controller is a container view controller that manages one or more child view controllers in a navigation interface. In this type of interface, only one child view controller is visible at a time.

What happens to toolbar items when the active view controller changes?

When the active view controller changes, the navigation controller updates the toolbar items to match the new view controller, animating the new items into position when appropriate.

What is a navigation controller object?

A navigation controller object manages an optional toolbar in its view hierarchy. When displayed, this toolbar obtains its current set of items from the toolbarItems property of the active view controller.

How do I hide the navigation bar in uinavigation?

To hide or show the navigation bar, use the is Navigation Bar Hidden property or set Navigation Bar Hidden(_: animated:) method. A navigation controller builds the contents of the navigation bar dynamically using the navigation item objects (instances of the UINavigation Item class) associated with the view controllers on the navigation stack.


2 Answers

2.UINavigationController stores view controllers in NSArray

That is not a given.

@property(nonatomic,copy) NSArray *viewControllers;

That in no way indicates that there is an ivar called _viewControllers or anything like it. It just tells us that there is some method -viewControllers that will return us an NSArray, and that there is some method setViewControllers: that will accept one, and hints that it will make a copy of it (or at least behave like it made a copy of it). That is all it tells us. If you expand an NSNavigationController in the debugger, you'll notice that there is no _viewControllers ivar listed there.

If you poke around a little, you'll find that -viewControllers is not implemented as a synthesized property. It just forwards onto -childViewControllers (which is a UIViewController property). OK, so doesn't that just move the problem? I mean -childViewControllers is implemented as [NSArray arrayWithArray:_childViewControllers]. Fair enough. You caught me.

But the same logic applies to [UIViewController navigationController]. This declaration:

@property(nonatomic,readonly,retain) UINavigationController *navigationController

does not mean that it actually has a strong link. It just means that if you called setNavigationController:, you would expect it to retain it. But you can't call setNavigationController:. There is no such method (not even a private one). So all this is really promising is that there is a method called -navigationController. And it's implemented as a call to +[UINavigationController _ancestorViewControllerOfClass:allowModalParent:]. That just passes on to the UIViewController implementation, which walks up the parentViewController chain looking for a UINavigationController. So there's no retain loop; it's dynamically determined.

But your question is still a good one. The header file here is confusing IMO, and I would open a radar against it. navigationController should be listed as assign or it should say nothing (even if that defaulted to strong it would at least not be misleading).

BTW, if this stuff interests you, you really should drop $90 for Hopper. It's very good at this kind of exploration.

like image 175
Rob Napier Avatar answered Dec 10 '22 08:12

Rob Napier


The viewControllers property of UINavigationController is defined as

@property(nonatomic, copy) NSArray *viewControllers

The view controllers currently on the navigation stack.

Then there is no problematic retain loop since the navigation controller removes any UIViewController from that array when the view controller is dismissed.

A retain loop is an issue when you do not have any mechanism to open the loop by releasing the retained object at some point (using release if non-ARC, or setting a strong property to nil).

like image 44
Vincent Zgueb Avatar answered Dec 10 '22 09:12

Vincent Zgueb