I can't seem to find any resolutions to this. I've made all my objects 'weak' in both view controllers in question. I have included dismiss functions and even the 'RemoveFromSuperView' function. I tried them all without luck. I also tried making the button action 'modal', 'push', ect. None made any difference.
Essentially as I move from controllers, memory seems to just accumulate more and more. On both controllers I'm simply using WebView's. I rack up over 100MB of memory usage after some time navigating between views. Eventually the app runs out of memory and crashes.
How do I either clear all memory accumulated by the app or properly dismiss/kill all inactive View Controllers and clear all memory associated?
This is how you override dealloc method
- (void)dealloc {
[_object release];
[super dealloc];
}
where _object is whatever property you initialized.
Related
I'm having a problem with a view controller that's dismissed and not referenced but still in memory, just wondering in general when is the object actually released in memory when no one references it?
The way I used to test is that I installed the PVC tool from Facebook and use it to print out the view hierarchy when the view controller is presented, after it's dismissed, I make sure no one's referencing it and paused the execution so I can po the memory address of the view controller from the previous PVC tool, but I can still see the view controller instance there.
Thanks!
You appear to be confusing being released and being cleared from memory. When the class is destroyed, the memory it occupied is not zeroed, just like when you delete a file in the filesystem, the disk blocks are not zeroed either.
This would simply take up too much time and have very little benefit.
Being released simply means the memory the class occupied can now be re-used.
One way to see if the class has been destroyed is to add a log in the dealloc method:
- (void)dealloc
{
NSLog(#"I'm being destroyed");
}
Custom KeyBoard get terminated due to memory pressure in iOS 8
Initially my custom keyboard is taking around 25mb of memory, but this memory is not deallocated with I dissmiss the keyboard. Memory keep on increase when we open custom keyboard again and again and finally terminated due to memory pressure.
Help me out with this issue?
You can dealloc some things in ViewWillDisappear function of KeyboardViewController
The keyboard extension runs in a process that persists after the keyboard disappears. Your keyboards view controller is created anew each time your keyboard is created, but the process that view controller is in persists. So free memory when your view controller is closed. If you are using images you won't want to use imageNamed: you will want to use imageWithContentsOfFile:. Because UIImage uses a cache for imageNamed that will persist.
I have tried tons of ways to avoid this famous memory accumulation issue, but according to my long long trial & errors, the best and the simplest way to free all memory before a keyboard disappears is to call exit(0) in viewWillDisappear of KeyboardViewController.
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
exit(0);
}
[Update] exit(0) was perfect to release all memory since it kills the keyboard extension process. Unfortunately it seems like killing the process makes iOS unstable.
Consequently, the most stable way is to release all allocated objects as much as possible in viewWillDisappear. For example,
For all custom views and all custom view controllers
Remove all strong references of the views and the view controllers, such as subviews, constraints, gestures, strong delegate, and so on.
[aView removeFromSuperview];
[aView removeConstraints:aView.constraints];
for (UIGestureRecognizer *recognizer in aView.gestureRecognizers)
[aView removeGestureRecognizer:recognizer];
Set nil to all object properties of the view controllers.
aViewController.anObject = nil;
For other big custom objects
Remove all added objects from all arrays, dictionaries, and so on.
[anArray removeAllObjects];
Do not cache images using imageNamed:.
If well released, memory usage while debugging would not be increased or very slightly increased(<0.1MBytes per dismissing). If memory usage is increased after many dismissing even though custom objects are released as much as possible, exit(0) can be called periodically with some risk of unloading.
I have memory issues with my iOS app and I have several questions about that.
Firs of all, I'm working with iOS 6 and I'm using ARC.
Now let me explain my situation :
I have 2 views. From the first view, if I tap on a button, I create the second view (using alloc and init) and I display it as a modal using this code :
[self presentViewController:secondView animated:YES completion:^{
[secondView prepareToDraw]; // Function I use to start my computations and rendering
}];
At some time, when computations are finished I want to close my second view and go back to the first view. I' using this code from my second view :
[self dismissViewControllerAnimated:YES completion:^{
[self finished]; // Function I use to free some malloc
}];
I run my application with Instruments Allocations and Leaks and I have no Leaks.
Here is the code of my didReceiveMemoryWarning :
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if ([self isViewLoaded] && ([[self view] window] == nil)) {
self.view = nil;
[self tearDownGL];
if ([EAGLContext currentContext] == self.context) {
[EAGLContext setCurrentContext:nil];
}
self.context = nil;
}
// Dispose of any resources that can be recreated.
NSLog(#"Resources freed");
}
The tearDownGL function frees OpenGLES resources like textures, vertex arrays, ...
When I run my application, after several switches between the first and second view I receive memory warnings and then my application crashes.
Here are my questions :
1- Is the application automatically freeing my UIImage, UIView, ... of my controllers? If not, how can I free them as I'm using ARC?
I also saw the viewDidUnload function but it's deprecated as the documentation says :
Called when the controller’s view is released from memory. (Deprecated
in iOS 6.0. Views are no longer purged under low-memory conditions and
so this method is never called.)
But if views are not purged anymore under low-memory conditions, how can I free more memory to prevent my application from crashing?
What should I do?
2- I put a breakpoint on the didReceiveMemoryWarning function for my 2 controllers. When I run the application on the simulator I simulate a memory warning.
I can see that the didReceiveMemoryWarning is called once for my 2 controllers.
But if I switch several times between my first and second controllers the didReceiveMemoryWarning is called once for my first view controller but is called several times for my second view controller. If I switched 3 times, the function will be called 3 times. So I guess, when I "close" my second view to go back to the first view, the second view is not freed and still exists. Why ? How can I force it to be destroyed ? (as I won't use it anymore and create a new one)
I create the second view controller in a function and I don't keep any reference to it (it is not stored in the class).
You should release (in ARC that means setting all strong references to nil) all memory (images, NSData objects, Arrays, all data represented by the model layer etc.) that are currently not required and can (easiyl) be re-created when they are used again. All of your other code should be written in a way that properties/iVars are checked for nil if those objects could have been released during a memory warning and then will be re-created.
I doubt that self.view is amongst that objects that might be disposal.
You may have displayed an UIImageView. That was probalby created with an UIImage object. You not not really need that UIImage in memory while the UIImageView is displayed. (If UIImageView still need it then it retains it or keeps a strong reference on its onw so that you don't have to worry about keeping the image itself.) THAT are the resources to be released.
If self.context is amongst the disposal resources, I cannot say. It may well be.
ARC does not always mean that the images, views ,etc gets released instantly. It gets added to the nearest arc pool and gets released.If the application may require it or uses it somewhere it gets added to the main pool which gets released only when the application terminates. So it is better to remove the object yourself if you think it has served it purpose. Especially in case of images it remains in the memory as it does not know whether it is getting used anywhere else or not.
Whenever you work with blocks, you should use a weak reference to self, since this can lead to retain cycles. So change your code to this:
__weak typeof(self) blockSelf = self;
[self dismissViewControllerAnimated:YES completion:^{
[blockSelf finished]; // Function I use to free some malloc
}];
Also, your code to free anything should be in dealloc. You don't need a custom method for it, if it only happens at the end of the lifetime of that controller.
Your first call also seems wrong:
[self presentViewController:secondView animated:YES completion:^{
[secondView prepareToDraw]; // Function I use to start my computations and rendering
}];
If prepareToDraw only happens once, when the controller is presented the first time, than you should run this code in viewDidLoad. This would also benefit your architecture, since only the controller itself should know what it has to setup in the beginning and to tearDown at the end.
Hope that helps. Perhaps you have other/more problems in your code.
Please have a look at auto-relase pools : AutoReleasePools
It reads :
Use Local Autorelease Pool Blocks to Reduce Peak Memory Footprint
Many programs create temporary objects that are autoreleased. These objects add to the program’s memory footprint until the end of the block. In many situations, allowing temporary objects to accumulate until the end of the current event-loop iteration does not result in excessive overhead; in some situations, however, you may create a large number of temporary objects that add substantially to memory footprint and that you want to dispose of more quickly. In these latter cases, you can create your own autorelease pool block. At the end of the block, the temporary objects are released, which typically results in their deallocation thereby reducing the program’s memory footprint
I had a similar issue, where I encapsulated all the big objets that I wanted to get rid of in an #autoreleasepool block.
I have a MonoTouch app that has a UITabBarController, with each of the tabs being a UINavigationController. Some of these wrap a UIViewController which adds a UITableView and a UIToolbar, and others wrap a DialogViewController.
I've not paid much attention to memory / view management thus far (I've been mostly running in the simulator), but as I've started testing on a real device, I've noticed some failures due to low memory conditions (e.g. the app gets terminated, and I discover from my log that DidReceiveMemoryWarning got called prior to this). Other times I notice prolonged pauses in the app's responsiveness that I am assuming are due to a GC cycle.
Thus far I've been assuming that every DialogViewController that I push onto the nav stack will clean up its views and other things it's allocated when I pop it. But I am starting to realize that it's probably not that easy, and that I need to start calling Dispose() on things.
Are there best practices for how to deal with managing resources and memory with MonoTouch and MT.D? Specifically:
Is it required to call Dispose on a DialogViewController after it's popped? If so, where is it best to do this? (ViewDidUnload? DidReceiveMemoryWarning? destructor?)
Does the DVC automatically dispose objects like the RootElement that is passed to it or do I need to worry about this? How about UIImages that it loads as part of rendering a table cell (e.g. StyledStringElement)?
Are there places where I should call GC.Collect() to better space out collections so as to not take a bit hit in responsiveness when a GC does happen?
Does the generational garbage collector help with the interactivity problems and is it stable enough to use in a production app? (I believe it's still billed as "experimental" in MonoDevelop 3.0.2 / MT 4.3.3)
What do I need to do in DidReceiveMemoryWarning to reduce the likelihood that iOS will shoot my app? Since each non-visible view controller seems to get this call, I'm assuming that I should clean up that view controller's resources... should I do the same kinds of things I do in ViewDidUnload?
I don't seem to get my ViewDidUnload called (even after I get a DidReceiveMemoryWarning). In fact I don't recall ever seeing it in my log. If iOS always called my ViewDidUnload after DidReceiveMemoryWarning, I could just do all the cleanup in ViewDidUnload... What is the best way to split cleanup responsibility between ViewDidUnload and DidReceiveMemoryWarning?
I apologize for the general nature of this question - this seems like a good topic for a whitepaper, but I couldn't find any...
Update: to make the question more concrete: after using Instruments and the Xamarin Heapshot profiler, it's clear to me that I'm leaking UIViewControllers when the user pops the navigation stack. Rolf filed a bug for this and it has two dups, so this is a real issue for more than just me. Unfortunately I haven't found a good workaround for the leaked UIViewControllers - I have not found a good place to call Dispose() on them. The natural place to free resources allocated by ViewDidLoad is in the ViewDidUnload message, but it never gets called on the simulator so my memory footprint keeps growing. On the device, I do see DidReceiveMemoryWarning, but I am reluctant to use this as the place to free my viewcontroller and its resources since I am not guaranteed that iOS will actually unload my view, and therefore not guaranteed that my ViewDidLoad will get called again either (leading to a ViewDidAppear which would need to code defensively against situations where its underlying resources were disposed). I'd love to get some advice on how to get out of this mess...
I've spent a couple of days in the MT.D source code and in the profiler. While I am still looking for general guidance on what the best design pattern is for implementing DidReceiveMemoryWarning and ViewDidUnload, I do have some general observations to share that could be useful for someone:
MonoTouch.Dialog is very well behaved. It does not leak any resources under ordinary usage. It keeps a control tree under DVC.Root, and each Element's Dispose method correctly Disposes the underlying UIKit control. You don't even have to worry about disposing an old RootElement if you've replaced DVC.Root - the property setter automatically disposes it for you. Overall, MT.D doesn't appear to suffer from any significant memory issues. There is one exception - see below.
When creating your own custom Elements (e.g. MultilineEntryElement), make sure to override the Dispose(bool) method, disposing the underlying UIKit control (e.g. UITextView), and chain the base class Dispose() method. The source code in Miguel's MT.D github project provides plenty of good examples. All the Elements implement the standard Dispose pattern (although they omit a destructor/finalizer that calls Dispose(false)).
When implementing custom view controllers, it is generally not necessary to implement Dispose on UIViewController subclasses, nor on TableView DataSource or Delegate classes. When the view controller gets GC'ed, it will correctly call Dispose on its references. All the cells that you allocate in the DataSource will be properly disposed.
As an exception to (3) - I encountered a nasty issue when adding my own subview to a TableView's cell. This subview is a control I created called "UICheckbox" that ultimately inherits from UIImageView, which has two UIImages (on and off) and a public event called Clicked. I only experience an issue when an event handler which references members of the DataSource is hooked to this event (if the event handler doesn't reference the DataSource or controller itself, all is well). However, when the conditions above are met, and the controller is dismissed, there is apparently some cycle that the GC can't figure out, and every UICheckbox I put on the TableView is leaked (along with its images). The only way I found to work around this was to add code to ViewDidDisappear to dispose of the ViewController and clean up its state IFF it is no longer anywhere in the navigation stack. It's hacky but it works.
In general, I adhere to the following template for allocating objects in my view controllers:
allocate nothing in the constructor (use it only to pass state in)
create a control tree in ViewDidLoad (and dispose it in ViewDidUnload). think "InitializeComponent" in XAML (if that helps). If the UIViewController is going to push a DialogViewController onto the nav stack, the ViewDidLoad is a good place to create the DVC.
initialize values in the control tree in ViewDidAppear. E.g. you can add/delete/replace Elements, Sections, and even the Root of the DVC in this method. But don't create a new DVC.
There is a general issue with leaking ViewControllers when the user navigates up the nav stack (I reference the bugzilla link in the "Update" in the question). This also affects MT.D. There is a fairly straightforward workaround - add the following line of code in ViewDidAppear of the parent view controller:
// HACK: touch the ViewControllers array to refresh it (in case the user popped the nav stack)
// this is to work around a bug in monotouch (https://bugzilla.xamarin.com/show_bug.cgi?id=1889)
// where the UINavigationController leaks UIViewControllers when the user pops the nav stack
int count = this.NavigationController.ViewControllers.Length;
Rolf does a great job explaining why this bug happens and why the workaround works in the bugzilla link, so I won't repeat it.
I hope someone finds this useful. I also hope someone smarter than me has some guidance on how to handle DidReceiveMemoryWarning and how to split work up between that method and ViewDidUnload.
Update:
A couple more notes:
I now realize the protocol for DidReceiveMemoryWarning and ViewDidUnload: the former is always delivered to every view controller, while the latter is only sent for view controllers that aren't currently displaying, AND aren't deeper than the root of the navigation stack. In the end, I decided to ignore DidReceiveMemoryWarning because I don't really have images that I cache and can dump (as per the iOS guidance). In ViewDidUnload, I release all the resources I allocated in ViewDidLoad.
My app has a TabBar where each tab hosts a UINavigationController, most of which push a DialogViewController. One issue I was dealing with was leaking the DialogViewController after the ViewDidUnload let go of the reference to it. I tried Disposing the DVC in ViewDidUnload, but iOS kept on wanting to reinvoke it and I was getting an exception for invoking a selector on a GC'ed object. I discovered the reason - the navigation controller was holding onto the DVC in its ViewControllers array. The solution is to release the array by creating a zero-length array in its place - in ViewDidUnload:
this.ViewControllers = new UIViewController[0];
The old array will now be GC'ed, and so will the DVC because nothing is pointing to it anymore. And iOS won't ever reinvoke the object. Note - no need to call Dispose on the DVC.
When my iPhone app receives a memory warning the views of UIViewControllers that are not currently visible get unloaded. In one particular controller unloading the view and the outlets is rather fatal.
I'm looking for a way to prevent this view from being unloaded. I find this behavior rather stupid - I have a cache mechanism, so when a memory warning comes - I unload myself tons of data and I free enough memory, but I definitely need this view untouched.
I see UIViewController has a method unloadViewIfReloadable, which gets called when the memory warning comes. Does anybody know how to tell Cocoa Touch that my view is not reloadable?
Any other suggestions how to prevent my view from being unloaded on memory warning?
Thanks in advance
Apple docs about the view life cycle of a view controller says:
didReceiveMemoryWarning - The default
implementation releases the view only
if it determines that it is safe to do
so
Now ... I override the didReceiveMemoryWarning with an empty function which just calls NSLog to let me know a warning was received. However - the view gets unloaded anyway. Plus, on what criteria exactly is decided whether a view is safe to unload ... oh ! so many questions!
According to the docs, the default implementation of didReceiveMemoryWarning: releases the view if it is safe to do (ie: superview==nil).
To prevent the view from being released you could override didReceiveMemoryWarning: but in your implementation do not call [super didReceiveMemoryWarning]. That's where the view is released by default (if not visible).
The default didReceiveMemoryWarning releases the view by calling [viewcontroller setView:nil], so you could override that instead.
What appears to be working for me was to override setView: to ignore setting to nil. It's kludgy, but then, this is a kludgy issue, and this did the trick:
-(void)setView:(UIView*)view {
if(view != nil || self.okayToUnloadView) {
[super setView:view];
}
}
Could it be so simple?
Even though nowhere in the documentation this is mentioned, it seems that if I exclusively retain my view in viewDidLoad, then it does not get released on Memory Warning. I tried with several consecutive warnings in the simulator and all still seem good.
So ... the trick for the moment is "retain" in viewDidLoad, and a release in dealloc - this way the viewcontroller is "stuck" with the view until the time it needs to be released.
I'll test some more, and write about the results
I don't think any of these ideas work. I tried overriding [didReceiveMemoryWarning], and that worked for some phones, but found one phone unloaded the view BEFORE that method was even called (must have been in extremely low memory or something). Overriding [setView] produces loads of log warnings so I wouldn't risk that by Apple. Retaining the view will just leak that view - it'll prevent crashes but not really work - the view will replaced next time the controllers UI is loaded.
So really you've just got to plan on your views being unloaded any time they're off-screen, which is not ideal but there you go. The best patterns I've found to work with this are immediate commit so your UI is always up-to-date, or copy-edit-copy, where you copy your model to a temporary instance, populate your views and use immediate commit with that instance, then copy the changes back to your original model when the user hits 'save' or whatever.
Because the accepted solution has problems with viewDidUnload still getting called even though the view was blocked from being cleared, I'm using a different though still fragile approach. The system unloads the view using an unloadViewForced: message to the controller so I'm intercepting that to block the message. This prevents the confused call to viewDidUnload. Here's the code:
#interface UIViewController (Private)
- (void)unloadViewForced:(BOOL)forced;
#end
- (void)unloadViewForced:(BOOL)forced {
if (!_safeToUnloadView) {
return;
}
[super unloadViewForced:forced];
}
This has obvious problems since it's intercepting an undocumented message in UIViewController.
progrmr posted an answer above which recommends intercepting didReceiveMemoryWarning instead. Based on the stack traces I've seen, intercepting that should also work. I haven't tried that route though because I'm concerned there may be other memory cleanup which would also be blocked (such as causing it to not call child view controllers with the memory warning message).