I want to know, what is the best way for lazy loading. For me, most of the applications i have used parsing and get the url from the server and put the image into the table view. So i have implemented lazy loading for improving the performance of the application. Now i want to know the best method for lazy loading images. Because i have used lazy loading into the four ways,
Lazy loading images from Apple developer.com
Implemented Asynchronous method for improving the lazy loading
Used separate main thread for handling the image downloaded.
Have used ECOImageLoadingDemo application for lazy loading.
But i have used the above four methods to achieve the lazy loading. But i want to know what's the best method for lazy loading. Which one is best for performance wise and memory wise is suitable for that?
Thanks in Advance.
Regards,
Pugal
From my experience, performance-wise and memory-wise solutions are on the opposite ends of a slider. You can move around with your solution somewhere between these two, but with the disadvantage that having the best solution performance-wise, usually means a worse solution memory-wise and vice-versa. I hope I explained this clear enough :)
Here's how I handle the problem of lazy loading images:
In my application I created ONE entity which I called GlobalImageProvider. All requests for images go through this entity. This way I have control on how many threads I use to download and I can implement a caching system (memory + local disk), all of these completely transparent to the application and with full control.
By controlling the size of the cache, I can control how quick the application feels. Performance wise, nothing compares with having an UIImage already created in memory.
Memory-wise, you can even choose to disable the cache.
Even more, I can even change the number of threads dynamically while the application is running depending on the quality of the network I have.
To make the online requests, I'm using NSURLConnection but I plan on switching to something else since I've read that it leaks memory.
On the view&controllers side, I have a AsyncImageView which is just a UIImageView that knows how to work with the GlobalImageProvider. It knows to display an activity indicator while loading and can handle the response from GlobalImageProvider.
If you know the URL of the image you want, all you need to do is add a AsyncImageView to your screen and make a request to the GlobalImageProvider with the AsyncImageView as the "handler" for that image.
If you don't like mixing data with the image views, you can add a ViewController between the GlobalImageProvider and AsyncImageView. He gets the image response and puts it in the ImageView.
That's about it, hope it helps you a bit.
You can take a look at this excellent tutorial from Peepcode in the section Bonus: KVO for Cell Images
http://peepcode.com/products/iphone-view-controllers-part-ii
Related
That may seem like a broad question, but I'm talking specifically here about apps that display a lot of content (image plus some text) pulled from the net in separate cells, rows, etc. And where each of those cells can get loaded asynchronously (independently from all the others).
So for example, for iOS, is it too inefficient to use uiwebview for each of these cells? It seems like this would be a simple approach, but I'm not sure about the performance. "Pulse" has a bunch of cells on the page at one time, and on iPad this could really get to a large number. Is it better to do this using lower level techniques, or is using UIWebView a decent choice?
Update:Clarification-->
Just to clarify, I understand that the typical approach is to use UITableView and then create table cells to hold and show the data (although the "Pulse" UI may be more involved). What I'm getting at here is if those UITableViewCells could hold UIWebViews? So instead of putting a UIImageView and a UITextView in each cell and so forth, if it makes sense to just put UIWebViews there instead, and give them URLs (could pass a param to indicated the row) to load as cellForRowAtIndexPath is called or something.
Well, if you talk about the YouTube app, its not UIWebview. The common and better approach is to use UITableViews along with the custom UITableCells. As far as asynchronous loading of images is the concern, we implement "Lazy Loading" mechanism. And in this way, we are able to keep the performance benefit of native UIView intact. Whereas, in UIWebView, you would need to design the iphone based webpages which will cost you learn about Dashcode tool, or may be someother opensource css library, etc.
In short, UITableView, with Custom UITableCells, along with Lazy Loading Mechanism (achieved by some Threading Techniques) will do all the Magic for you.
Hope this answer will give you some better idea....
Do not use UIWebViews with UITableViews like that. There are far too many moving parts for a UITableView!
I wrote the NYTimes iPhone app while I worked there. I only ever used a UIwebView to display article content. This is because NYT articles and log post layout is extremely complicated and impracticle to replicate programmatically. (I did override some of the CSS in app)
The UITableView is a super high performance implementation for butter smooth high speed scrolling. UIWebViews are the opposite of that: heavy, slow and memory intensive. In fact web views actually run internal virtual machines and threads (for JavaScript, rendering, etc).
Keep in mind that table view cells are recycled very aggressively. The instant a cell scrolls off screen bottom it will, in gneral, be immediately reconfigured for use as the new cell that scrolls in from the top.
With a UITableView, you need to create VERY highly optimized custom UITableViewCells. The data displayed in the cells should be cached aggressively by you so that you don't need to recompute it or perform intensive layout calculations. Uncacheable information such as remote images should be loaded asynchronously (which might mean they show up much later).
On caching:
The NYT apps have a high performance caching system for images. The trick is to request the imge data as soon as its "needed", but to also preempt downloading images which were needed a second ago, but no longer. You also don't want to cancel partially downloaded images because that wastes the already consumed bandwidth. Check out NSOperationQueue. It has most of the levers needed to build such a system.
Another note: consider building a web app if your project may also be accessed via web or an android app.
Suppose you have several animations that we want to achieve with a sequence of PNG in a UIImageView.
If we have many images in the animation we have a delay from the moment we send the message [myImgView startAnimation] this becausethe loading of all images in memory.
I noticed that the loading is lazy: as long as the startAnimation message is not sent images are not loaded into memory.
To avoid the problem of delay i load all the animation in the app delegate, attach as subview and than animate once. I want to understand what is the best solution? And if my actual solution have a draback?
You're right about lazy loading. I've never been able to determine if its actually lazy spooling from disk or lazy decompression but in either case, a UIImage (and, underneath, a CGImage) is not necessarily fully processed and ready to draw until it is actually used. I assume that it may conceivably become unready again in the future, depending on exactly how Apple handle memory warnings internally.
If you wanted to be really keen, I guess the best solution would be to use CoreGraphics to load and decompress the images in the background, pausing to finish loading upon startAnimation only if loading hasn't already finished. You can't achieve that directly with UIKit objects since they're not callable anywhere but on the main thread. You'd need to get a CGImage, draw to a bitmap context then create an image from the bitmap context and post that onto the main thread to be wrapped into a UIImage. And it'd probably be smart to use an NSOperationQueue to marshall the complete list of operations.
Supposing you don't mind the additional startup cost of blocking while you load all the PNGs, and are dealing with memory warnings correctly, there shouldn't be any problems with your current approach and I can't think of a better solution while remaining in high level Objective-C stuff.
In my application I use lots of images based in interface builder. The problem with this is that it uses large amounts of memory because interface builder caches it much liked "imageNamed" so I've begun removing the image from imageViews in interface builder and instead adding them once the view starts using "imageWithContentsOfFile". After several hours I have made little progress because I have literally hundreds of images. I'm just wondering if there is a more straightforward way to do this?
Yes, don't do it. UIImage and the whole xib business pretty much delay loading until things are needed, as well as drop cached images where possible and needed. You can even see this happening in Instruments. It helps to split your design over several xibs, so they can be loaded when needed.
What you can do however, is to make sure that you don't scale images but display them 1:1, and that you save them in the lowest acceptable quality. For photo's, take JPEG. For other images, take PNG.
I have a method for loading images for UITableViewCell in the background. I use performSelectorInBackground. The problem is these threads are finishing and loading images even though they may not be on the screen anymore. This can be taxing on resources, especially when the use scrolls quickly and lots of cells are created. The images are fairly small and being loaded from the disk (sqlite db), not from a URL.
I've put code in the cell to check to see if it's the most recently displayed cell and I don't load the image if it's not. This works, but it's still creating the threads even though the "expensive" work of loading the image from disk isn't being executed unless it's the most recent cell.
The question is, what's the best way to deal with this? Should I kill existing threads each time the UITableViewCell is reused? How do I go about killing threads invoked by performSelectorInBackground?
Any other suggestions on how to handle this are appreciated.
Have you looked at EGOImageView?
You might consider just loading one image at a time. You can still do it on a thread but serialize the loads so as to not over-burden the system.
You could add the visible cells to an array when they become visible, when cells become invisible you can remove them from the list (or just check if they are visible at convenient times). You could also try deferring the load for a brief amount of time to avoid loading an image that is just scrolling by. The thread would pop the first item from the list and load it, then queue another load.
For threading technologies you could have a look at Operation Queues or make a dedicated thread with NSThread. The Concurrency Programming Guide provides a good overview. Although there is nothing wrong with the method you are using.
Sharing data between threads will also require some form of locking to avoid simultaneous access.
First: I've implement a fairly complex scrolling mechanism for images, that allows to scroll over a few hundred thousands (theoretically) in one single scroll view. This is done by preloading small portions upon scrolling, while re-using all UIImageViews. Currently all I do is to assign new created UIImage objects to those re-used UIImageViews.
It might be better if it's possible to also re-use those UIImage objects by passing new image data to them.
Now the problem is, that I am currently using the -imageNamed: method. The documentation says, that it caches the image.
Problems I see in this case with -imageNamed:
As the image gets scrolled out of the preloading range, it's not needed anymore. It would be bad if it tries to cache thousands of images while the user scrolls and scrolls and scrolls.
And if I would find a way to stuff new image data into the UIImage object for re-using it, then what happens with the old image that was cached?
So there is one method left, that seems interesting:
-initWithContentsOfFile:
This does not cache the image. And it doesn't use -autorelease, which is good in this case.
Do you think that in this case it would be better to use -initWithContentsOfFile:?
Only a benchmark can tell you for sure. I'm inclined to think that UIImage image caching is probably extremely efficient, given that it's used virtually everywhere in the OS. That said with the number of images you're displaying, your approach might help.
I'd say YES. You have too much images to keep all of them in cache so you can't use -imageNamed:. If your images are not displayed many times, you will not get lower performance.
I found one link regarding this which comments of imageNamed method http://www.alexcurylo.com/blog/2009/01/13/imagenamed-is-evil/