I have singleton. My singleton has UIViewController property. When I push some view controller I set pushed view controller to the singleton property.
For example I pushed B view controller from A view controller
and inside B view controller initialization code i set the property of singleton:
inside init code:
Singleton *singleton = [Singleton sharedInstance];
singleton.viewController = self;
This code means even when I pop back to previous controller A the instance B never be destroyed as I think and seems I will have memory leak.
So each time when I will push B controller I will increase memory usage.
How can solve it. I have tried use weak instead of strong for singleton property but I am not sure that is the solution.
The expected way for me - something like cascade destroying. But maybe I confused and this code will not cause memory leak. What do you think.
Not really. You aren't leaking the instance because you still have a reference to it. And the memory usage doesn't increase because next time you push B it will set itself to the singleton and replace the previous instance (which will then be deallocated).
Generally, if you do want to store the reference, you should make it 'weak' and / or have the view controller remove itself when it is removed from its parent.
Related
Some of my view controllers don't get deallocated after being popped from view. I've gotten rid of the other strong references so I'm left with this internal retain cycle held through a reference form _externalObjectsTableForViewLoading. It's a private UIViewController property so I'm unable to clear it myself. I don't know if iOS has an API to clear it or why it's not being cleared after popping the view controller.
I've tested with with my app running in Release mode both in iOS 11 and 12. Running the app in Instruments renders the same stairs pattern seen in Xcode with the view controllers being retained.
Any ideas? Thanks in advance!
In your problem, is one viewController accessing another viewController? Our problem is that there was a non weak reference to a callback in another viewController.
As mentioned in your and in other posts on this, _externalObjectsTableForViewLoading is a viewController private property, but a storyboard related property. This leads me to think that your code has strong references to another object that is a view controller through a callback or through a direct property reference to its instance.
I am trying to understand unwind segue process.
Is dealloc method of the source view controller called when unwind segue performed like UINavigationController back button process?
If not, does that mean a memory leak?
Not directly.
It will be called when all strong references to the view controller are relinquished.
After unwind is called, the uinavigation controller SHOULD be finished with the view controller, as long as you haven't retained it strongly in some sort of navigation controller or something.
It doesn't always happen right away, think autorelease pools too.
Ensure you do not have any strong delegate outlets either. (big culplrit)
A useful tip is to look for anywhere in the view controller's code 'self' is used, and determine if it is strong, if it is you must nil it in unwind or dealloc will not get called - you shouldn't have to worry about dealloc too much with ARC - unless you have some objects you need to manage or non auto-nil weak references.
I have code that replaces an entire stack of ViewControllers in a NavigationController, as sort of a "reset" that switches between a number of different threads of UI scenes. Instead of creating a different NavigationController for each flow in storyboard, I decided to just change the NavigationController.viewControllers array manually:
self.navController.viewControllers = [vc1, vc2]
What happens to the old stack of ViewControllers? Do they automatically get release/unloaded? or do they hang around forever and therefore causing memory leak?
Note:
I know about unwind segue, but I didn't want to use that since even if I unwound to the first VC in NavVC, I still have to replace it with a different VC, and setting .viewController is the only way I know to not result in a "Back button"
What happens to the old stack of ViewControllers?
The viewControllers property points to an array and has copy semantics, so when you assign an array of view controllers to that property, the navigation controller copies the array. When you assign a different array to that property, the previous one will be deallocated (assuming there are no other strong references to it). The same is true for any of the view controllers in the array -- if nothing else has a strong reference to them, they will be deallocated. This is no different from any other Objective-C object.
After you set the new view controllers, your UINavigationController will lose its strong reference to the old stack. If you do not have any remaining strong references to the items in the old stack, they will indeed be released by ARC.
One way to verify this behavior is to override -(void)dealloc in the view controllers you want released, and put some logging in there. you should be able to see when dealloc is called.
my project use ARC, and when i have to show a view with navigation controller i do this:
ShareViewController_iPhone *share = [[ShareViewController_iPhone alloc] initWithNibName:#"ShareViewController_iPhone" bundle:nil];
[self.navigationController pushViewController:share animated:YES];
and i can see in xcode that the memory is increased of a certain value, then when i dismiss the view i do this:
[self.navigationController popViewControllerAnimated:YES];
but when the view is closed, the memory doesn't decrease, how i can do it?
When you pop the viewcontroller, it is marked for deallocation (i.e it's reference count becomes 0 unless referenced by another object). but not deallocated immediately. Deallocation as far I have seen is mostly random!
The view controller here must be retained for a while by the navigation controller itself to create the animation. The deallocation does happen immeadiately after the reference count drops to zero. Try to pop the view controller without animation (e.g. animated:NO) and see for yourself if the deallocation happens right away.
ARC will clean the memory on its own cycle (it will collect all the objects whose reference count set to "0" means who are ready for deallocation), we can't control this. It is managed by the operating system.
So when you are popping a view controller, it doesn't deallocate it from the memory immediately. It can take time depending on the other memory usage/memory availability.
One more case, you need to check if on every push the memory allocation is increased by a certain value, then there will be definitely something wrong in your code.
When you pop a ViewController and it don't release, it means have a reference cycle in your controller. You must check delegate if have, block, weak, strong reference.
Your delegate must be weak reference.
If use block. The self must be weak reference: __weak id weakSekf = self;
Hope these information will help you. :)
I have an iOS-App which uses ARC. I don't use InterfaceBuilder, all UI is generated manually. In that app I have several UIViewControllers with SubViewControllers. Those ViewControllers a tied together from a menu (-ViewController) who pushes them on the stack.
My problem is, that memory doesn't get freed when switching between the ViewControllers.
Is it wrong to keep references to the SubViewControllers like this?
#property (nonatomic, strong) UIViewController subViewController1;
#property (nonatomic, strong) UIViewController subViewController2;
viewDidUnload never gets called. Has anyone a good example how to build a clean view hierarchy?
By assigning the view controllers which get pushed on the stack to a strong instance variable / property, they will not be deallocated when popped off of the stack. The strong properties are holding on to the pushed view controllers even after they are popped off the stack, so they never get to a state where they can be deallocated.
Normally I do something like the following when pushing a next-level-down view controller onto the navigation stack:
SLSMoleculeSearchViewController *searchViewController = [[SLSMoleculeSearchViewController alloc] initWithStyle:UITableViewStylePlain];
[self.navigationController pushViewController:searchViewController animated:YES];
Under ARC, the new view controller will be allocated and will be retained on creation. When pushed onto the navigation stack, it will be retained once by the navigation controller. Because this new view controller is not referred to after being pushed, and is not assigned to a strong property or instance variable, ARC will release it after the second line.
Remember, it's still being retained by the navigation controller, so it's still live in memory. However, once the navigation controller pops it off the stack this view controller will be released. Since nothing is holding on to it at that point, it will be deallocated as expected.
If for some reason you need to maintain a reference to this sub view controller in your higher-level view controller, you could use a weak property or __weak instance variable. This will not hold on to the view controller, and will turn to nil once the controller is deallocated.
weak references are only supported for applications that target iOS 5.0, though, so you won't be able to do this for anything that needs to run on iOS 4.0. The 4.0-compatible unsafe_unretained property is not something I would recommend in this case, due to the danger of a pointer to deallocated memory.
This most likely has nothing to do with ARC. viewDidUnload is only called on a view controller when the view property is released / set to nil and this typically only happens if the app receives a memory warning.
Try triggering a memory warning in the simulator and see if that causes your viewDidUnload method to fire. If it does then everything is fine. If not, you are probably over-retaining your views somehow, perhaps by assigning them to other strongly retained properties.
There are exceptions to the view-retaining policy, for example the UINavigationController frees up views in its view controller stack if they aren't frontmost, but it does that by simply setting the view of its child controllers to nil when they're covered by another controller's view.
If you want your views to be released when they aren't onscreen, either set the controller's view property to nil in the viewDidDisappear: method, or stop retaining the view controllers when their views aren't onscreen and just create fresh controller instances each time you need to display them (that way both the controller and view will be released when not in use).