I am working on a medium-sized app and notice a lot of memory increase when moving back and forth between components.
I put this on the component:
destroyed(){
console.log(this
}
I was surprised to find a lot - if not all - the 'data' still there plus computed properties. I thought this was supposed to be clean at that point. What all should be expected to be destroyed by that point? Is this a cause for concern and a potential source of memory leaks?
Related
We are able to confirm that the render() method of our component is getting invoked. We also see that the data that needs to be shown is correctly passed in via props. However, the actual phone display won't repaint the updated UI until it is touched.
Interestingly this only happens in the production build not on the development builds of the app. Sigh.
We have seen this in the past when updates are done from InteractionManager.runAfterInteractions, but in this case we have removed every use of runAfterInteractions and are still seeing this behavior.
Using react native 0.57 but also seeing the same issue on 0.58.
I can provide more specifics if needed, but wanted to know if anyone has seen anything like this before and what if anything they did to fix such an issue.
I had this issue also when the content that should repaint was inside a 'ScrollView'. I refactored my code using a 'FlatList' instead of a 'ScrollView' and the problem disappeared.
It was a struggle, but I figured out what was happening.
TL;DR
The solution is to wrap your redux action dispatches inside a setTimeout(..., 0) whenever you are doing UI updates from a function passed to runAfterInteractions or from a Realm callback.
Some quick background:
I was dispatching a redux action inside a Realm callback. The action would in turn cause the UI to re-render, except the rendered UI would not repaint on screen until it was touched.
Similar behavior was observed when you dispatch redux actions from a callback passed to InteractionManager.runAfterInteractions. This makes sense because callbacks passed to runAfterInteractions wait for UI to do its thing and are then executed as a setImmediate batch. These callbacks were intended for heavy background jobs that would otherwise block UI rendering. So if one were to do UI rendering in such callbacks those would themselves get halted like a background job!
Solution:
The point of the realm callback is to notify you that some data has changed, so that you can re-render your UI. However, the realm callback was behaving like one passed to runAfterInteractions and so UI re-rendering was getting held up. I took a leap of faith and decided to move the Redux action dispatching code to a different queue, namely setTimeout. Here's what the fix looked like:
...
// assume this is in a callback passed to runAfterInteractions
// or in a realm callback
// PUT YOUR REDUX ACTION DISPATCHES IN A setTimeout
// callback
setTimeout(() => {
getFilteredCartAreas(dispatch, realm)
dispatch(new ActionCartAreaCardChanged())
}, 0)
...
The fix is to do the UI rendering logic inside a callback passed to setTimeout. Notice the zero delay; the goal is to simply move logic to a different queue that is not held up by UI interactions.
How can I track down abandoned memory if in Xcode Instruments don´t show my custom classes?
So I can see the heap is growing after I perform an action with my app (open a view and go back to the previous in my case) and I could fix some memory issues before by tracking down my mistakes in my code.
Now however, I only see things like in Generation N (= Heapshot N), non-object and I don´t know any more how I can fix the leaks. The responsible caller (not seen in the screenshots) also show no own classes/objects.
Edit
The Showing View is a UIWebView. I searched the web and found rumors that UIWebView doesnt properly releasing data. Could that be the issue? I can´t find any solution.
UIWebView is notorious for causing memory issues.
Make sure you set the UIWebView object's delegate property to nil if you assigned an object to it as documented in the class reference. You can do this in dealloc.
Cleanup the web view in viewWillDisappear:animated: by stopping URL loading with a call to stopLoading and/or setting the HTML string to nil by calling loadHTMLString:baseURL: to workaround any memory being held.
I'm working on a kiosk style slideshow app. I have a UIScrollView which shows the slides, and a factory class, which generates the slides. The "slides" themselves are UIViewController subclasses, which are loaded out from XIB files and customized by the factory class. In my main view controller, I set up the scroll view and start a timer. The timer calls a "reload" method every N seconds, which handles the reload and call to the factory class.
The method that the factory class uses looks something like this:
- (SlideViewController *)slideFromManagedObject:(Slide *)managedObject{
NSInteger slideType = [managedObject slideType];
switch(slideType){
case kSlideTypeA:
{
//
// configure arguments here
//
return [[SlideViewController alloc] initWithArgument:argument] autorelease];
break;
}
//
// More types here...
//
default:
break;
}
}
I haven't yet gotten to the point of defining all of my cases, but the ones that are filled out seem to cause jumps in memory usage. If I add return [[[UIViewController alloc] init] autorelease]; right before the switch/case, I get no visible view, as expected, but I also don't see those memory increases. I'm not sure, but I suspect that it's the "C blocks" that I'm wrapping my slide generation code in.
Some things to note:
When the app starts, I see the memory plateau from about 400 kilobytes to around double that. Then, when the slides progress, any of the slides whose generation code is contained in curly braces is called, the memory plateaus upwards again.
This behavior only seems to happen once per launch - when the app loops through all of the slides, the plateaus to_not_ happen again. However if the app is backgrounded and then relaunched, the plateaus do occur again, consuming even more memory.
When I left the app to run overnight, for about 10 hours and forty minutes, the memory usage had slowly climbed from about 1.44 megabytes to somewhere closer to 1.57 megabytes. I suspect that there are/were some other leaks in there that may have been fixed by my tweaking, but the main jump from about 800 kilobytes to somewhere between 1.4 and 1.5 megabytes is still an issue.
Instruments does not report any leaks, but the plateauing concerns me.
What could be causing the increased memory?
EDIT:
So I don't think it's the blocks, since using an if/else seems to do the same thing.
Here's a screenshot of the Allocations instrument running:
Where could possibly be holding on to these views?
One possible explanation for what you are seeing is some caching that UIKit (I assume) is doing of your objects (don't know what they are, but I think of images mostly).
Caching is often used during transitions and for other internalities of UIKit.
UIKit empties its caches usually when a memory warning is received, so you could try and send one to see what happens. In actuality, I suspect that results of sending a memory warning will not be very easy to analyze, since all of your views are also unloaded, hence memory will go down forcibly. But you can try...
As to how sending a memory warning to the device (as opposed to the simulator), here you find an useful S.O. post.
I have an application with lots of view, about 20, accessible from two main menus, in each view there are labels, textfields and buttons, over a background image.
As data i've a global class and some array filled by xml files.
If memoryWarning fires, I should release what is not necessary, but if you are in one view, all in the other view is already deallocated right? What should I deallocate? Removing the global class i will lose essential data, the same for array.
Suggestions? (Sorry for bad english).
The views you have left are not necesserily deallocated, it all depends on the code. So you possible hold everything in the memory at the same time and memory is a scarse resource on iOS. So if you have a lot of views deallocate those that you don't currently use.
After receiving memory warning you should remove everything that you not need. Keep an eye on those XML files in arrays, XML files tend to be big: as the source and as a DOM tree in the memory after being parsed. You can try to make them all being lazy loaded and parsed, throw them away if not more needed, reload if needed again (you have then to save their loading state, nut this could be a simple array of boolean value instead of whole XML data).
Here is my scenario:
I am building a location finder using the iPhone mapkit. I have an array stored in the application delegate to hold the information about the location of a store (name, address, etc.). When a certain button is pressed, a view slides in with a textfield and a button which performs a lookup of the users input, and returns all of the necessary information.
All of this works fine and the points get plotted onto the map. However, if I go and try to do a search a second time, the application crashes. I am trying to remove all of the objects from the array when the xml parser begins:
- (void)parserDidStartDocument:(NSXMLParser *)parser {
[dataTempForSearch removeAllObjects];
}
and the debugger simply puts an arrow on the method call with no real explanation as to why...
Has anyone run into a scenario like this before? any thoughts as to why this might be happening only on the second time the action is performed?
It's hard to tell based on just that one line of code.
It's probably a memory management issue, but what specifically I can't say.
Shots in the dark:
I would destroy and re-create my parser each time I did a search.
I would also clear the dataTempForSearch immediately after passing the data to the app delegate, not right when you go to do another search.
MapKit has some very nasty problems. When you get that odd behaviour of the debugger putting an arrow at that line, take a look at the call stack provided to you (this can be seen in debug mode on the left side usually). I'll bet you it's to do with MapKit.