So I installed my app on my ipad and it crashes due to a memory issue. I figured this was because I'm using really big image files so I went back and reduced them all and essentially cut out about 75% of their size by resizing them and then using PNGCrush.
Now, as for running the program, if I have a background image for each of my 4 individual tabs, would it save memory if I were to set the images to Null every time I switch tabs or should I leave them set? I have one page which has a couple dozen images on it as they act as buttons and from what I'm hearing it sounds like I should clear them all when I'm not viewing that page. Is that correct?
Right now when I boot up, I load all of the images for everything in the app in a sort of: "load it now and be done with it" mentality, though from what I've been reading that causes memory issues as there isn't much memory to use. Does this mean every time I switch tabs or views I want to clear all the images from the ones that aren't visible and then reload them when we go back to them? Would this cause an increase in performance? Or at least prevent crashes? My program works in the simulator but when I run it on my ipad it just explodes =/
Thanks!
EDIT: I'm using Monotouch BTW hence everything is in C#
UIImage BG = UIImage.FromFile("Images/Makes/explosion.png");
UIImage basic = UIImage.FromFile("Images/Models/camaro.png");
UIImage advance = UIImage.FromFile("Images/search.png");
AdvancedSearchButton.SetBackgroundImage(advance, UIControlState.Normal);
ImageSearchButton.SetBackgroundImage(basic, UIControlState.Normal);
MainBG.Image = BG;
BG.Dispose();
basic.Dispose();
advance.Dispose();
Now I know in regular C# dispose() doesn't actually "free" memory, but I read something that says that it gets converted to a "release" when it compiles over to obj-c so that would essentially be freeing those objects.
Also, I'm wondering if I would need to dispose() of the individual buttons and the image after I'm no longer viewing them. I was just setting the image to NULL but that gave me errors.
MainBG.Dispose();
AdvancedSearchButton.Dispose();
ImageSearchButton.Dispose();
Thanks so much for the help!
EDIT2:
So I just tried the above code and the background images and everything else are still there and appears as if nothing is actually getting cleared. Suggestions?
The iPhone does not have virtual memory and it does not have garbage collection. So once something is loaded in the memory it stays in the memory until your code explicitly releases it. If you are not using some resources, you should definitely clean them up as soon as possible.
Also, you should listen to low memory warnings from iOS, which is another opportunity for your code to do some internal clean up.
First off, lazy load your resources unless you have a justifiable reason not to. Secondly, I'm not sure how big your images are, but on large images (ones that are by nature intended for things like backgrounds, or otherwise), generally speaking, I will chop them up into chunks, and load them, again you guessed it, lazily as they're needed.
What you should do is handle your memory warnings properly. Deallocate any resources that are not absolutely critical -- i.e., items on other views in other tabs, or hidden content. You can load them again when you have to.
You should also take a look at using Instruments to find out if you leak (I'm hoping you've already done this), examine your program to see if you can persist some cached resources to disk during low memory situations, etc.
Related
I’m facing memory issues in my app and I didn’t find a way to find out yet which objects/classes are using that memory.
The app is simple, a view controller with a gallery view of images (grid view just like Instagram Explore; collection view with xib cells) and when you tap one, it takes you to the next screen, which is the same set of images, but as a vertical list (uitableview with xib cells). The images are downloaded asynchronously from web.
The memory used by the app is continuously increasing when I scroll in both screens and also faster every time I open the list screen. Then the only moment when the memory used is reduced (and I mean drastically, like from 1.8GB to 200MB) is when it hits the device’s limit and then the issue appears again and again. Also, sometimes, the system fails to reduce the memory used and the app crashes ("Terminated iOS app due to memory issue").
I don’t think it’s a layout problem, I’ve checked all of that, also used memory graph debugger and only “malloc” issues found there which takes me nowhere, no class, no line, no nothing. Also, Instruments tool is too complex and I don’t know how to deal with it yet.
I’ve read some tutorials and tried some solutions but nothing worked. There are included: https://krakendev.io/blog/weak-and-unowned-references-in-swift, http://iosbrain.com/blog/2018/07/22/finding-memory-leaks-with-the-xcode-memory-graph-debugger-and-fixing-leaks-with-unowned/, https://www.youtube.com/watch?v=1LnipXiSrSM&t=1697s, https://developer.apple.com/videos/play/wwdc2018/416/
Can somebody give me some other advice or tutorials on how to properly debug memory issues to be able to find out their exact origin?
Most contemporary memory debugging strategies are geared for identifying and resolving strong reference cycles. But that’s not your issue here. The fact that most of the memory is recovered when you face memory pressure points to caching issues. Whatever further diagnostics you do is only likely to confirm this behavior.
To address this, set reasonable limits to your caches and avoid caches that don’t give you that control (e.g. UIImage(named:)) and the problem will likely be resolved. We cannot comment further without seeing how images are being retrieved (e.g. make sure the cache for the URLSession is reasonable) and how they’re being cached once downloaded (e.g. third party async image retrieval libraries generally give you control over the cache).
And, assuming you (or your third-party libraries) are caching, make sure that:
Test your app on the simulator, manually choosing “Debug” » “Simulate Memory Warning”. This will help confirm whether the app is responding to memory pressure. On the basis of what you describe, I think we already know this is the case, but it’s a good diagnostic.
Note, while we always want to make sure our apps respond to memory warnings correctly, when you face memory warnings, it may already be too late (e.g. the app may be doing a series of allocations and may fail before your app has a chance to react to the warning). You want to do whatever you can to manage caches before memory warnings occur.
You’re caching the original payloads (the Data object that contains the compressed jpg/png asset) and not UIImage objects (which, once they’re used, are uncompressed and can be huge if you’re not careful), or
If you do cache the UIImage objects, make sure to resize them as appropriate for your UI.
For example 100x100 image at 3x scale will take up 120kb, but if the image is 1000x1000px, even if the image view is only 100x100pt, the uncompressed image will take 4mb, i.e. 4 bytes per pixel, regardless of size of compressed jpg/png payload.
If you are using NSCache, set countLimit or totalCostLimit.
If you are doing your own collection (array or dictionary) of downloaded images, make sure you respond to memory pressure. E.g., in Swift:
NotificationCenter.default.addObserver(forName: UIApplication.didReceiveMemoryWarningNotification, object: nil, queue: .main) { [weak self] _ in
// do whatever you need to remove your cached objects here
}
I don’t think that’s the issue here (because your app is responding to memory pressure), but you might want to review your app for anything else that (a) is large; (b) you are downloading and holding in memory yourself, and respond accordingly.
FWIW, I think you’ve done sufficient diagnostics to identify the source of the problem (the fact that it’s getting purged under memory pressure really does point to caching issues), but if you want to learn the “Allocations” tool in Instruments, check out these old WWDC videos Fixing memory issues and iOS App Performance: Memory. They’re old and focus on Objective-C, but the techniques outlined there are still applicable if you want to get up to speed on Instruments. But, like I said, I think you’ve already identified the issue.
I'm having a memory issue with one specific ViewController and memory. When I launch my app in debug mode, it launches with 40mb memory usage. (I have no idea if this is already a lot or not -- what is common?). Then, when I open this specific view, it spikes up to about 120mb. The issue is, when I pop this view (with the navigationController popViewController), the memory stays up at 120mb. And then, when I re-open it, it spikes to 200mb (a 80mb increase every time).
With other similar ViewController it spikes up to 120mb too, but when I close the view, it goes back down to more or less 40mb.
The problem is that this specific view contains quite a bit of code (about 1000 lines...) and it's impossible for me to post everything here.
What methods should I use to specifically locate the issue in xCode?
For anyone I might be able to help with this:
use the tools in xCode as recommended. There are some great tutorials online.
In my case it was an issue with an [NSTimer] which kept a strong reference to my view, so it never got released afterwards, thus stacking up memory. Make sure to stop your time when you pop a view.
I'm building an application that requires a bunch of local images to be displayed in the imageview of a uitableviewcell. However, i'm having difficulty optimizing the performance of the uitableview. I've noticed two issues specifically: first, the view takes a while to load. Second, the scrolling gets laggy when new cells are displayed.
The viewDidLoad is loading in the images like this:
for (Object *object in self.objects)
{
object.thumbnail = [UIImage imageNamed:object.imageName];
}
this is obviously causing the long-load issue, but I'm not sure how else to get those images loaded. Is it a size issue? is this just a bad way of doing it?
The process of displaying the images also seems to be problematic, in other words, even after the images have been assigned to the thumbnail property, they still take too long to be drawn.
Although this is a specific case, I'm curious more generally on how apple loads images in photos so efficiently. Any insights? thanks
Whenever I find that my UI is lagging, the first thing I suspect is that I am performing some operation synchronously (on the main thread) that should be performed asynchronously (on a background thread).
I am also very curious as to how exactly Apple is achieving that performance in the photo app. I am writing an app that has similar requirements as yours right now. My current approach is to load a bunch of photos from disk into memory asynchronously as soon as the user opens my view controller, and continue to load (and remove photos) from memory - ahead of time - as the user scrolls.
Currently, I load each photo from disk asynchronously in cell for row at index path, which is pretty fast, but causes this cascading effect to happen if you scroll quickly through the table view. That is, the cell will appear empty for a moment before the photo appears.
I hope this sheds some light for you.
You may also be interested in trying SDWebImage - which includes an image cache object that has been making my life easier when dealing with local photos.
They are doing everything in the background on various detached threads most likely. This will allow for a great deal of fluidity in applications that are hosting/presenting a great deal of information. I have created a photo gallery myself in various applications, loading many photos simultaneously from different web APIs and whatnot, and by simply creating new threads and managing the allocation efficiently and accurately, you can get a very smooth interface/interaction
Well, the long-load issue is caused by this part of your code - UIImage imageNamed:, because this method caches all the images on the same thread, and also could crash the app if the memory is overloaded.
Try looking at this library - it should do what you are trying to achieve :)
http://www.cocoacontrols.com/platforms/ios/controls/ktphotobrowser
I am new to IOS and coming from pcs I am trying to get a feel for managing memory and battery resources.
I have a UIImageView subclass which is is very light (it's an image placeholder with a handful of methods). This subClass is repeatedly added to, and removed from, several root views. I hold an IBOutlet reference to the subClass on each root view.
Would you:
Simply add/remove the subClass from the root view (and keep it in memory)?
Or
Create and destroy the subClass instance each time (freeing the memory but extra work)?
Is this worth considering or am I worrying over nothing?
Would your answer be the same if the UIImageView subClass was very large?
Is there a way of measuring these matters?
In general, you can use UIImageView with disregard on iOS. UIImageView is not much more than a clipping region with some methods for dealing with images.
The resource which takes up more memory is the UIImage contained by the UIImageView and these are cached by iOS.
So there is little cost to creating UIImageViews and destroying them, just take care that you dealloc them and don't leak memory and you should be fine unless you have hundreds of them.
The subclass being "large" doesnt matter, the code in a class is never loaded more than once. If the class has alot of extra state - that will take up some memory but I cant image a whole lot of that in a UIImageView.
You better just keep it in memory. User experience will be much better if you don't slow down the device by continuously recreating the image view. One single poor UIImageView instance is not memory heavy enough to worth the time and effort to recreaty all the time.
Keeping it an memory to improve performance isn't as much of a hit as you would think - taken from the other answers you have. But you should remove it from memory if you get a memory warning, and provide a way to bring it back to life as needed.
I would go with your first option, keeping it in memory. This is just because I think it is handy to keep my views alive and reuse them. This means that they will remember their state and content and that helps out in many cases. I usually write methods to reset the content or refresh it when needed.
Keeping it in memory might also increase the loading of the view slightly, but usually you do not notice the difference, and I see people using both ways.
And yes, I would say you are worrying a bit too much, especially if you think this decision would affect battery.
Regarding memory management, keeping the imageview in memory or releasing/recreating consumes the exact same amount of memory as long as you only have one imageview that you are using. The only difference is that you are allocating and releasing it repeatedly if you are recreating it.
You could however get memory issues if you start creating new imageviews every time the user opens them and never letting go of them, but that means you are doing something fundamentally wrong in your programming.
As long as you know what you are doing, no need to worry about the amount of memory in a case like this - there's plenty. Start worrying if you are handling large images.
Especially if you use ARC there is not much to worry about, just start hacking away. If you are managing memory manually, I do recommend trying to reuse stuff because that causes less headache and risk of giant leaking. Use leaktools and analyze tool to make sure in this case.
While profiling my iOS app I perform a simple task multiple times where I push and pop a view from the stack, and then take a heapshot. The same code is executed each time and the pushed view just contains some images which get removed as soon as the view gets popped.
Now because the persistent count is 0 for almost half of the heapshots, would it be safe to assume there is no abandoned memory?
I've heard that the rows with a persistent count greater than zero could be because of some internal caching.
I am using Xcode 4.2.1 and the app is using ARC.
Edit:
Thanks to Kendall Helmstetter Gelner I've managed to debug this problem further. I probably shouldn't have been using the simulator for debugging this. I've now run it on the device and it now looks like there is one object which isn't getting released:
After looking through the extended detail view I think I've just about found the cause of the problem.
It's hard to say from just that screenshot what might be cached or not - UIImage will cache things, possibly some other system elements too. It looks like you might be running that in the simulator, have you tried heapshot on a device?
You could also select "Hide System Libraries" to the side and get a better idea how much of your own code is involved.