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]; }
Related
I can perfectly load my map view with annotations the first time. However, if i try to reload the map on a button click (after its already loaded), the user has to wait till the process completes.
This problem has arisen because on reload, the new annotations dont appear until the mapview is moved significantly, that's when the viewForAnnotation fires. I've seen two other questions similar to mine with solutions involving 'performSelectorInBackground' & 'performSelectorOnMainThread'. The former didnt work for me :( & the latter i dont want to do (though it's the only option that works) as i want the user to be able to interact with the map while the annotations load without blocking the main thread. I'm aware that such animations are best done on the main thread, so the question(s) 1. Is there no other way to do it than having the user wait till the map reloads? 2. Suggestions on the best way to do it?
Thanks in advance.
You can use a dispatch queue block to achieve this
here is the syntax
You can create your on private queue like this
dispatch_queue_t queue = dispatch_queue_create("com.MyApp.AppTask",NULL);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue,
^{
//do the fetching of data here(Don't do any UI Updates)
dispatch_async(main,
^{
// Do the UI Update here.
});
});
Apple has referred to this as recursive decomposition.
In this bit of code the computation is offloaded onto a background thread with dispatch_async() and
then dispatch_async() back into the main queue which will schedule our block to run with the updated data that we computed in the background thread.
App receives event from child thread, but we need to send it to main thread to do UI update issue, current now use method dispatch_async, dues to main thread is very busy, for some case it will cost two or three seconds to arrive at the destination, slow, try to improve, but so far no better method found, any idea or discussion will be appreciated, thanks in advance.
dispatch_async(dispatch_get_main_queue(), ^{ handleLoginOK(value); });
Look into -performSelectorOnMainThread:withObject:waitUntillDone: it may or may not be faster, I am not completely sure.
I want to run a single background thread for an iPhone application which is available in background all the time and gets executed when specific event fires and go to wait for specific event to fire to start its execution again. During the execution of thread if specific event is fired again then thread should restart its work.
I am working on a custom map application. On TouchesMoved event, I need to load the map image tiles according to the positions moved in a background thread. The problem is that when I move the map with speed the touchesMoved event is fired the previous thread has not finished its work and new thread is started. It causes thread safety issue and my application is crashed.
So I am thinking of a solution to have a single thread all the time available and starts its work when touchesMoved is fired if touchesMoved is fired again it should restart its work instead of starting a new thread. I think it will prevent the thread safety issue.
Please help
Firstly I'd echo the use of NSOperation and NSOperationQueue. You could fall-back to using NSThread directly, but the point of NSOperation is that it hides threading from you, leaving you to concentrate on the processing you need to do. Try firing NSOperation requests as and when required, see what the performance is like in your use-case; even if these operations get data in an async manner, it should provide you with a cleaner solution with good performance, plus future proof.
I've successfully used NSInvocationOperation to fire requests as often as required, and it sounds like the sort-of requirements and behaviour you're after. I would suggest more generally that you experiment with these in a test project; here you can test performance.
The following weblog's helped me start playing with NSOperation:
http://www.dribin.org/dave/blog/archives/2009/09/13/snowy_concurrent_operations/
http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/
As always, the Apple Threading Programming Guide is a key read, to figure out which way to go depending on needs.
This sounds like an ideal job for an NSOperationQueue. Have a read of the operation queue section of the concurrency guide.
Essentially, you create an NSOperation object for each map tile load and place them on a queue that only allows them to execute one at a time.
Put a run loop in your background compute thread. Then use an NSOperation queue to manage sending messages to it. The queue plus the run loop will serialize all the work requests for you.
I am currently saving images to the iphone's local space whenever I finish loading them. I was wondering if I need a separate thread to do that. I.e
The iphone can be requesting multiple images at the same time and when they are loaded, I call the save to HD method.
Since there are a lot of save to HD method being called at the same time, does anyone think I need a separate thread for each save to hd calls or somekind of queue that stacks the objects before being processed ?
Thanks.
There is NSOperationQueue class which allows you to run operations in background:
http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/NSOperationQueue_class/Reference/Reference.html
See this answer: NSURLConnection delegation and threading - 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.