ScrollView content async downloading problem - iphone

I have UIScrollView with lots of UIImageView inside. In the loadView method I assign some temporary image for each of subview UIImageView images and starts several threads to async download images from internet. Each thread downloads and assign images as follows:
NSData *data = [NSData dataWithContentsOfURL:URL];
UIImage *img = [UIImage imageWithData:data];
img_view.image = img;
Here is the problem - I expects picture will changed after each image downloaded by I can see only temporary images until all images will downloads. UIScrollView still interact while images downloads - I can scroll temporary images inside it and see scrollers and nothing blocks run loop, but downloaded images doesn't updates..
What I tried to do:
Call sleep() in the download thread -- not helps.
Call setNeedsDisplay for each ImageView inside ScrollView and for ScrollView -- not helps.
What's wrong ? Thanks.
Update. I tried some experiments with number of threads and number of images to download. Now I'm sure -- images redraws only when thread finished. For example - if I load 100 images with one thread -- picture updates one time after all images downloads. If I increase number of threads to 10 -- picture updates 10 times -- 10 images appears per update.
One more update. I fixed problem by staring new thread from the downloading threads each time one image downloaded and exit current thread (instead of download several images in one thread in the cycle and exit thread only when all downloaded). Obviously it's not a good solution and there must be right approach.

I would discourage use of threads for this usage, and use an Asynchronous API for data retrieval on the network.
NSData *data = [NSData dataWithContentsOfURL:URL];
This function call sends some data out a socket then sits there and locks the thread until it receives a response. If you don't make any synchronous calls you don't have to worry as much about locking the UI Thread and your problem becomes much simpler.
I would use the ASIHTTP Library or NSURLConnection to access your data, and do all of your updating on one thread. There is a slightly learning curve with the API's but it's substantially smaller than managing threads correctly. I would strongly recommend the ASIHTTP Library, it just rocks. You can set the request delegate to a progress widget and throttle your connection for edge usage.

Hi Newbee (no phun intended)
The situation you describe sounds like a candidate for NSOperation and NSOperationQueue
You can simply "load" everything off to these methods and have them figure out how best
to retrieve the picture. If you keep building new threads, then at some point you loose the advantages of threading, i.e. the overhead grows for each thread and there is only so much CPU time to go around.
Start out by sorting the list of URL's for the pictures so that the ones furthest away from the user (in a scrollView sense) get's loaded last, then start adding the load operation off to an NSOperationQueue and then start the Operation. These classes will then balance things out and save you a ton of coding the logic to deal with scenarios that could erupt.
I found this helpful some weeks ago:
NSOperation tutorial
Marcus Zarra lays out a nice simple example to get you started.
EDIT: Whoops... and to answer your question:) if this does not result in your pictures updating, then you might need to send a specific "update" message when the operation is done.
If this is a large part of your app I would recommend you build a PictureObject and move the functionality inside that. So when adding a PictureObject you instantiate it with a URL and it displays a dummy picture as it's view. The you hand this PictureObject over to your NSOperation who retrieves it's URL loads the picture and sets it on the PictureObject.
This way it is async all the way and you don't have to deal with loops or testing if picture is downloaded etc. etc.
Hope it makes sense.

Related

NSData dataWithContentsOfURL slow

I have some performance problem with NSData dataWithContentsOfURL...
NSURL *url = [NSURL URLWithString:Imagepath];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img=[UIImage imageWithData:data];
[ArrayImages addObject:img];
This code is placed in a method that manage a JSON response got from an NSUrl connection(after calling my web service). All the code in this method is already in a background thread, moving this piece of code out the background thread do not solve the issue.. All the retrieved image are placed in a view in main thread. What i can do to make dataWithContentsOfURL faster or there is a alternative to dataWithContentsOfURL?
Thanks In Advance
+[NSData dataWithContentsOfURL:] is not "slow". If loading one image takes a long time, the problem lies elsewhere.
Evaluate your problem. For starters:
Which resource is the bottleneck? Probably the Network.
How do you load images? All at once? That would be bad -- display them as they become ready.
What are the sizes of the images? I saw one SO question where the poster wanted to load 50 MB images. That's way too large. As well, if all you need is a thumbnail, then be sure you request the thumbnail from the server and load that and not the full-sized image.
Are you loading things you don't even need to display? Wait until you need to display them.
How many threads are you using for Network tasks? For CPU? For I/O?
Are your source images properly "crushed"?
Write your program so it flows with your program's presentation model. Example: I had a bazillion images to display in tables, but I made sure to minimize resource usage and made sure load and request cancellation was well supported for the app. This was all coming through the network, and it was plenty fast (it was network-bound).
and if you are loading many images from device storage, you should consider using -[UIImage initWithContentsOfFile:] instead because your image data would not be cached, but can be purged.
U need to use Lazy loading for images display as content will displayed as image gets downloaded.
Use SDWebImage which uses lazy loading of images.

Memory is increasing all the time in navigation based iphone app

I have a navigation based App with 5 ViewControllers. After inspecting the memory allocation with instruments i found out that memory is increasing permanently. The app starts up with 2 MB and after navigating through all 5 Navigationcontrollers it grows up to 10 MB and counting...
For every allocated object there is a corresponding release in my code (in dealloc or somewhere else), so obviously there are no memory leaks. Instruments also tells me that no memory leaks could be found.
How can I solve this memory problem? At some point of time, after navigating through the views forward and backward an out of memory warning occurs and the background image disappears (I do not deallocate the image so the iphone does it on its own).
How can i determine the source of the "memory leak" problem? Is it possible that images are cached somewhere and hold in memory as long as the app is alive?
I always alloc images like this:
UIImage *kaufpreisImage = [[UIImage alloc] initWithContentsOfFile: [[NSBundle mainBundle] pathForResource:#"kaufpreis_background_green18px" ofType:#"png"]];
The images are under the Resource folder.
I would very much appreciate your help! I can't find a way out of this!
Thank you very much in advance
Regards Phil
Edit:
The only problem remaining is when I try to send an email. I want to send an email which contains multiple html tables and images inside the email body. The images are base64 encoded. After sending multiple mails I recieve an out of memory warning, although I dealloc all the stuff (images, Numbers and so on) inside the sendMail() method.
Does anyone know if sending base64 encoded images leads to problems? Can anyone give me a hint how I can send images inside the emailbody (e.g. as background image inside a html table like style="background-url(data:image/png;base64,%#)")? To clearify, this works. The images are sent to the Client (tested on MacOS) but sometimes the memory problem occurs...
Thank you very much
You need to release those images once you take ownership of them.
Maybe you shall try the 'Heapshot' function in the Allocation tool. Simply take a 'Mark Heap' when your first controller is started then navigate on and again back. Take another heapshot. Repeat this process 8-10 times. In the heapshots list you shall see that some memory has been allocated between an heapshot and the following one (otherwise if the amount is small and tends to zero it is normal), try to inspect it in order to check if you allocate something that remains alive.
You are responsible for releasing the instances of UIImage if you have used [[UIImage alloc] init...] to create them.
If you are loading images from the main bundle, try using +(UIImage *)imageNamed:(NSString *)name. This UIImage returned is returned autoreleased, so you don't have to release, and there is the upside that UIKit handles caching when using the imageNamed method.

running several background threads in succession on iphone / ipad

I have to download a ton of images and I am doing it on a background thread, problem is all of the downloaded data is not released until I go back to the main thread which is fine a for couple hundred images but when I get into thousands the app runs out of memory and crashes.
So I need to run several background threads in succession so I can batch the download of the images in groups of say 200 so my autorelease pools can clear and memory gets released.
I cannot wrap my head around how to do this. I need some sort of recursive function on the main thread to call the background threads with and keep track of the batches so it knows which to call next. I know that passing values back and forth between threads will cause me some problems so I am not sure how to approach this?
Anyone tackle a problem like this before?
NSOperationQueues solve this problem for you. You create a class that descends from NSOperation that does the download for you. You then create an NSOperationQueue and add the downloads to the queue. You can control the max concurrency of the queue, etc.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperationQueue_class/index.html
You don't need to wait for everything to download before releasing memory. You could just pass each image or small batch of images to the main thread using performSelectorOnMainThread, and let the main thread release memory as it caches the data in storage or uses the data. Then continue on in the background thread until done and pass a "done" message, again using performSelectorOnMainThread.
Use NSInvocationOperation. tht wuld fix the issue instead of splitting the code in chunks. here is the sample i used
NSOperationQueue *downloadQueue = [NSOperationQueue new]; for (Product *cProduct in productsMasterArray) {
NSInvocationOperation *downloadOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:#selector(downloadImages:) object:cProduct.ITEM_CODE]; [downloadQueue addOperation:downloadOperation]; [downloadOperation release]; }

Save sequence images to iphone

I make screen shot with iphone camera use the UIGetScreenImage()method, I want to make this images sequence to video, but the memory is limited , i think write the image data to documents maybe a good choice for me. so when capture the image use UIGetSreenImage(), start a new thread to write the image data to documents, but this will delay the thread which used to capture image.
I don't know how to deal with this issues, Would you give me some advises? any reply will appreciate.
Foremost, I'd stay away from UIGetScreenImage() if you can, at least if you plan on releasing your app to the App Store. UIGetScreenImage() is not a public API, and it's one of the most likely "forbidden" APIs that Apple would reject your app for using.
If you want a screen shot (i.e. what the user sees on the device's screen), I think the approach that you've laid out works well, but you're going to need to get a graphic representation of the UIView that contains all your other views.
All UIView code must be on the main thread, though. You can do your file I/O on the background thread, but the actual getting of the image will have to be main thread.
How about NSOperationQueue?
Whenever the screen is captured and an image is created, an NSOperation which writes an image into documents is added a NSOperationQueue.
And I think you should find the best number of the max concurrent operation count (NSOperation's setMaxConcurrentOperationCount).
The max concurrent operation count depends on your app.

Using performSelectorInBackground to load UITableViewCell image in background, performance

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.