I'm using a lot of small images that are placed into UIImageView objects. Currently, I'm trying to foresee the problems that might occur later, such as memory warnings, staying in background and etc. My app is quite small - less than 10Mb but still, I'm a little bit concerned. The reason is that I'm not sure how my app will cope with the situation when a lot of UIImageViews are loaded on screen, then app goes to background, and then after a long time comes back. Do I have to reload all on-screen UIImageViews?
I have read that iOS will cope with UIKit data (such as image views) itself when backgrounded. And that I do not need to worry about restoring this though. It would be OK if my UIImageView objects would use PNG image files directly from app directory. But the thing is I'm using spritesheets (files with several images packed inside), then pulling those images with CGImageCreateWithImageInRect, and then passing to UIImageView at some time point. Now, I'm questioning myself, will iOS cache my on-screen UIImageView objects, or do I have manually to reload those UIImageViews when app goes active? I understand that if a lot of apps will be running in background then my backgrounded app might be killed. But I'm worried about the situation when my app will be still alive (in background) but with released UIImageView objects...Please share you knowledge and insights :)
Current implementation of "background" apps do a complete snapshot of your app memory. Such approach guarantees that app will be restored after complete killing from a memory in unchanged state. This mean that you do not need to do any additional manipulations such as persisting of code-generated images etc to support restoring of the app. UIImageView will not be unloaded automagically if you will not implement a clean-up logic inside of applicationDidReceiveMemoryWarning for UIApplicationDelegate instance. However an auto clean-up logic implemented for cached UIImages loaded thru imageNamed method.
Related
My app utilises tableviews that all have associated UIImages.
I show my UIImages in the app using:
cell.foodImage.image = UIImage(named: foodImageArray[indexPath.row] + ".jpg")
I know in objective c something such as
UIImage initWithContentsOfFile
Could be used in order to keep the memory footprint low.
Is there something like this I can use in swift?
UIImage(named:) caches once loaded image and keeps it in run-time until app quits or 'out of memory' notification from system, on which cache somehow cleaned up automatically.
Thus it is better to use this constructor only for small images, which are either always visible in UI or used often.
For other cases (probably your table view case is here) it is better to use UIImage(contentsOfFile:) constructor with implemented some in-app cache logic to keep it for some workflow only but clean when go out it (other mode, scenario, etc. including force cleaning for 'out of memory' notifications)
At a great number of requests from people using older iOS hardware, I'm currently refactoring and optimizing my app so it will work on iOS 3. That being said I've got a glitch with my UITabBar that I can replicate on all of the iPhone 3G units I've tested it on.
The glitch appears to have been fixed in iOS 4, but I was wondering if before that time, anyone else had this glitch as well and had figured out a (relatively elegant) workaround for it.
The problem is what you can see below; when a memory warning occurs and all of the views offscreen are released, when I bring a view controller with a tab bar back on screen, all of the UITabBarItems that are supposed to be in it are gone. As far as I can see, they're not being drawn at all; ie tapping the tab bar has no effect. After setting breakpoints and examining the UITabBar and its items in memory, they're all still there (ie not getting released), just that they're not getting redrawn when the UITabBar is re-created in the controller loadView method.
My app works similar to the official Twitter app in that I implemented my own version of UITabBarController so I could control the integration of it with a parent UINavigationController properly. I set it up as closely as possible to the original UITabBarController class though, with all of the child view controllers handling their own respective UITabBarItems and initializing them inside the class' init methods. Once the child view controllers are passed to my TabController object via an accessor method, the tabBarItems are accessed and added to the UITabBar view.
Has anyone seen this behaviour before and know of a way I can fix it? I'm hoping there's a really simple fix for this since it already works in iOS 4, so I don't want to hack it up too badly.
Thanks a lot!
After a bit of research, I think I found a solution to this. It's not the most elegant solution I was after, but it definitely works.
I'm guessing after a memory warning is triggered, something is happening to the UITabBarItem objects that basically renders them corrupt. I tried a lot of things (flushing out the UITabBar, re-creating the controllers array etc), but nothing worked.
I finally discovered that if you completely destroy the UITabBarItems and allocate new ones in their place, then those ones will work. :)
So my final solution to this was to add an extra condition in the viewDidLoad method of my controller that if the detected system was iOS 3, and there was already an array of UITabBarItems, it would go through each one, copy out all of the properties needed, destroy it, allocate a new one and then copy the old properties over to the new one.
I'm still going to keep an eye out for a better solution (I think there's a bit of overhead in this method), but thankfully at this stage, iOS 3 legacy support is becoming less and less of an issue. :)
This is a multiple part question, mostly because my ignorance on the matter has multiple layers.
First, I put together a caching system for caching CGImageRef objects. I keep it at the CGImageRef level (rather than UIImage) as I am loading images in background threads. When an image is loaded I put it into a NSMutableDictionary. I had to do a bit of arm twisting to get CGImageRef's into the array:
//Bunch of stuff drawing into a context
CGImageRef imageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
[(id)imageRef autorelease];
[self.cache setObject:(id)imageRef forKey:#"SomeKey"];
So, as you can see, I'm trying to treat the Image Ref as an NSObject, setting it to autorelease then placing it in the dictionary. My expectation is this will allow the image to be cleaned up after being removed from the dictionary. Now, I am beginning to have my doubts.
My application clears the cache array when the user "restarts" to play with different images. Running the application in Instruments shows that the memory is not dropping back to the "start" level on restart, but instead remains steady. My gut tells me that when the array has all objects removed the CGImageRef is not being cleared.
However, I'm unable to confirm this as I don't quite know how to track down the actual source of the memory in instruments. It's just a list of (Malloc 16 Bytes, Malloc 32 Bytes, etc), drilling into them just show a list of dyld callers. Not sure how to properly read it.
So, first question, is my way of caching CGImageRef objects completely flawed? And is there a better way to confirm such things in instruments?
First of all, caching CGImages is OK and I don't see any problems with the code you posted.
Am I correctly assuming you use an NSMutableDictionary as the cache? If so, you can clear it by sending it -removeAllObjects, which should release all the keys and values. If you just set different images for the same keys, memory usage may remain roughly the same because you replace previous images with new ones. If the images have the same size, memory usage should be constant except brief spikes when you create a new batch of images.
As for Instruments, I've seen it both report false positives and miss real leaks. Try running it several times, making pauses, if possible, for the Leaks instrument to "catch up". This sounds crazy, but I think it may make it a bit more reliable.
If all else fails, you can log the contents of the cache before and after loading a set of images to make sure the cache itself works as expected.
Why not just cache UIImage objects; you can make them fine on a background thread?
It's UIImageView objects that you have to be more careful with and even they are OK for most operations in the background.
I've watched the WWDC 2010 talks about adopting multitasking and I have started enabling our app, however I have a question regarding releasing non-visible views.
According to the talk (Session 105 - Adopting Multitasking on iPhone OS, Part 1 - 00:34:50) it is stated that the system releases "non-visible UIViewController views" when entering the background. However during my testing (in the simulator), I'm not seeing this happen.
The -viewDidUnload methods aren't being called, and after entering the foreground and viewing a previously hidden VC, the -loadView method is not being called either. So the hidden VC views in my UITabBarController aren't being released at all, contrary to what they said in the talk. Any ideas?
There's no mention of this in the "Moving to the Background" documentation, only in the talk.
Any clarification would be greatly appreciated.
Have you tried simulating a memory warning while your app is in the background? I believe that the views are only released when other apps start requesting memory. If memory consumption stays the same, your views are not released so that you can switch back quicker.
I have a large number of UIViews that are created in the app delegate applicationDidFinishLaunching and added to an NSMutableArray. The user pages through these views using a page control and scroll view. I created the UIView and its child components in IB. They are wired to a controller. To create 170 views on the iPhone takes about 30 seconds. Eventually, I'll have over 1000 views. Besides being slow, it will also kill the app because of memory use. How much faster and memory efficient is creating everything programmatically without views? How might some of the 6000 fact type apps be doing it?
Is there a better approach instead of creating everything at once? The user may access a view in slot # 400 and begin scrolling from there. Any suggestions are appreciated.
UIViewControllers are lazy. They only load when requested, and automatically unload when memory is tight (and it's easy to unload them manually by calling self.view=nil).
"Load" here means "read a NIB," or "build programmatically." ViewControllers don't really care. Building programmatically can be a little faster since you don't have to hit the disk, but this is seldom the bottleneck, since you only display one view controller at a time.
As to which way to go, it's more style than performance (with the exception of UITableViewCells, which there are reasons you need to build programatically in most cases).
Start by studying the View Controller Programming Guide. It'll show you how iPhone intends you to do this.
To eJames' comment about NIBs being XML files, this may be slightly misleading. NIBs are binary files generated by compiling XIB files which are XML. I'd do actually profiling on the phone before assuming that NIB load time is actually a problem. Much as I am by nature inclined to programatic layout, I've found in practice that NIBs greatly simplify many UI issues in practice, and I always come back to them for large projects.
It is difficult to suggest an answer without knowing a little bit more about your specific problem, but I would venture to say that if you expect to display 1000 different things, creating 1000 individual views in IB is not the way to go.
If your pages share a common layout, you can use a UITableView to display the content of each page, and store only the data for each page in your NSMutableArray.
An excellent tutorial on how to use UITableView can be found here.
If they do not share a common layout, laying things out programmatically will be the way to go. This should be no more memory or processor intensive than doing it using IB, and in fact it will probably be faster, since it removes the need to read and parse an XML file (which is what .NIB files actually are).