iphone - Why the NSURLConnection connectionDidFinishLoading is not running? - iphone

My app looks like this:
I have a UIScrollView and there is imageview inside. I implemented the scrollViewDidScroll delegate so that if the scroll did scroll, I will create a NSURLConnection and download a data from some url, for only once.
The problem is that the NSURLConnection connectionDidFinishLoading only be called or run after I release my finger on the scroll view.
To be more clearly, If I use my finger to scroll the scrollview, I confirm that the NSURLConnection is created, but as long as I do not release the scrollview, the connectionDidFinishLoading is never called. Once I release my finger, the delegate of connectionDidFinishLoading is called.
Anyone can explain this to me please?
Thanks

That's because during scrolling the UI enters a modal loop processing the user scroll inputs, and until scrolling finishes your thread won't get back to the message queue to pick up any delegate messages from the NSURLConnection.
You could run the NSURLConnection in a separate thread, which should keep pumping messages when the user is scrolling, but you'd still not be able to call across to the UI thread. The way round that might be to have the connection thread write the data into a lock-free data structure that the UI thread can then read - but this is likely to be complicated!

Related

Understanding setNeedsDisplay/drawRect with Blocks

I'm trying to understand how things work in regards to concurrent programming and calling setNeedsDisplay. I basically have Three objects.
Main View - container with different UIView objects, the main one being a UIScrollView
Small Map View - a small UIView that draws a miniature version of one of the other UIView items on screem
Processor - a delegate of the Main View that calculates what's on screen and calls the Main View back with what's in view.
So a simple use case of what's going on is the user touches the ScrollView and then the processor updates what's in view of the scrollView (like calculating coordinates, center point, etc) It does this using blocks and does it asynchronously. This then posts a notification to the MainView object.
When the MainView receives the notification, it just calls
[smallMap setNeedsDisplay]; // example 1
I put some logs around this call, and I do see it gets called right away. However, the drawRect: of this function does not get called right away. It gets called after 2 seconds or so.
I remember reading that setNeedsDisplay just marks the view for redraw to happen on the next event of the run loop.
But if I add this code instead:
// example 2
dispatch_async(dispatch_get_main_queue(), ^{
[smallMap setNeedsDisplay];
});
My view gets redrawn right away.
I guess I'm confused as to why I have to ask for the main event loop to call setNeedsDisplay to immediately redraw something. Like in example 1, by me calling setNeedsDisplay, is that done in the background or something and that's why it doesn't get redrawn right away? I'm trying to understand the difference in what's going on behind the scenes so I know what to look for the in future. Like should I have all my calls that need to be immediately redrawn in something similar to the example 2 block? Or is it because I'm processing my data asynchronously that I need to then ask for the main queue? Thanks!
My guess is 1 of 2 things:
Your code that is running on a separate thread is calling your MainView methods from the separate thread instead of using performSelectorOnMainThread or a GCD call that invokes the code on the main thread. Thus your call to setNeedsDisplay is actually taking place on a background thread, which is a no-no, as the other poster said.
The second possibility is that your MainView code is running on the main thread, but it gets busy doing time-consuming processing, or waiting for a synchronous call to another thread to finish, and doesn't service the event loop.
You can rule out the first possibility by setting a breakpoint on your call to setNeedsDisplay and looking at the call trace in the debugger to see what thread it's running from.
Figuring out the second possibility will take a little more work. You might need to delve into instruments.
setNeedsDisplay is a UIKIT API call and has to be called from the main thread of the application, also known as the UI thread. That's why calling it in a background thread doesn't have any immediate effect and scheduling it on the main queue has immediate effects.
See this related question https://stackoverflow.com/a/6988115/172690 for a more detailed answer.

NSOperation finishes in the background, attempts to notify main thread, view no longer exists. Crash

I have an NSOperation running not in the main thread. It is spawned from a UITableViewController. When the operation is complete, I'd like to reload the tableview since some data has changed. I've set a delegate for the background to notify on completion. When done, I call a wrapper around reloadData specifically on the main thread using performSelectorOnMainThread.
For the most part, this works well, however, there is a non-0 chance that the original (edit)tableViewController (/edit) gets released and I get zombie calls.
So the question is in 2 parts:
Is it possible to have a delegate from the background thread without retaining the object?
Is this just a bad design? Should I be using NSNotifications instead? Would that be the preferred method of notifying in this case?
Thanks in advance.
A delegate should be retained if there is a possibility that it might be released before any operation on the delegate is invoked. You can set up a state in tableViewController to handle the case when the delegate callback is invoked and the tableViewController is not to be used (Basically make the callbacks act as no-op). Once your operation is done, just release the delegate object.
It is not a bad design but you just need to handle these conditions.

UIView takes too long to update

In an iPad app, I have a bunch of UIImageViews inside a bigger UIView. Each UIImageView contains a thumbnail that is generated in a separate thread (so as not to freeze the application). After thumbnail has been successfully generated I call setNeedsDisplay on main thread, however, it doesn't update the UIImageViews as the thumbs become available (I can see them in the log), rather it takes about 5 seconds and then displays all of them at once.
here's what I am doing when a thumbnail has been created in a separate thread:
[self performSelectorOnMainThread:#selector(setNeedsDisplay)
withObject:nil
waitUntilDone:NO];
any ideas?
What's your main thread up to? If your application waits until all the thumbnails are available before redrawing, then it sounds like maybe you are inadvertently blocking on your main thread until your thumbnailing queue has emptied. How are you setting up the threads?
The problem is that you can't tell cocoa that it have to redraw "now".
With setNeedsDisplay you can onely order a redraw, because drawing is ra rather expensive procces.
setNeedsDisplay
You can use this method or the
setNeedsDisplayInRect: to notify the
system that your view’s contents need
to be redrawn. This method makes a
note of the request and returns
immediately. The view is not actually
redrawn until the next drawing cycle,
at which point all invalidated views
are updated.
Mybe its a better and more performant solution to wait for all generated thumbnials?!
I would use NSNotification. Send a notification from your thread loading the images when an image is ready. Your view controller can observe these notifications and update the view as they arrive.

subview is not added immediately (iphone)

When the return button on the keyboard for a textfield is tapped I want to add a UIView, then connect to a website with NSURlConnection sendsynchronousrequest and I have the code in that order
But when I run in the simulator (I can't run on device) the connection is run first then the subview is added (ie the opposite of the order of the code)
Why is this and how can stop it, because I want the view to added, then the connection done and then the view removed.
The subview is being added, but views are drawn by the runloop. By making a synchronous request on the main thread, you are blocking the runloop, so the view won't be drawn until after the request completes. Do the request asynchronously, either by using the async API or by doing a synchronous request in a background thread.
Many actions happen on the run loop, rather than in the order you code. If you really want to code the way you have, then performSelector:withObject:afterDelay: with a delay of 0 might work to trigger your NSURlConnection (you will need to move that code to a method).
As JK suggests, an asynchronous request might solve it anyway, and improve the UI. I'm a great fan of ASIHPPTRequest library, which makes async trivial.

Refreshing a UITableView

I have a UITableView subclass and a UITableViewCell subclass that I'm using for cells. I'm bulding all my cells in advance and store them in an array from where I use them in cellForRowAtIndexPath. Aside from this I have a thread that loads some images in each cell, in the background. The problem is that the cells don't get refreshed as fast as the images are loaded. For example, if I don't scroll my table view, the first cells only get refreshed when all cells have been modified and the thread has exited.
Any ideas on how I can effectively refresh my tableview/cell?
Have you tried calling [cell setNeedsDisplay] but on the main thread?
setNeedsDisplay when called on a background thread does pretty much nothing,
try this:
[cell performSelectorOnMainThread:#selector(setNeedsDisplay) withObject:nil waitUntilDone:NO];
Are you using a callback to notify the controller of your tableview that the images have been loaded? If not, that would be an ideal method.
When the image loads, fire off a callback to the table view controller that sets the image on the cell, and then calls reloadData on the tableView.
This way whenever a new image loads, the table will update to display it.
Not sure exactly what you are trying to achieve with the images - but can I guess they are coming from a server and that is why you want to download them in another thread?
I would not try to load up the cells before you display the table - you should use lazy loading as much as possible to make sure you are making the most of the memory on a device.
My suggestion would be to look at using a subclass of NSOperation to manage the loading of images. Firstly NSOperation will handle all the complexity of threading for you and allow you to queue up the operations. You will then be able to prioritise the operations that you want completed for the cells at the top.
As each operation completes you can make a call back to the cell or tableViewController (perhaps create a delegate protocol to make this really easy).
If you have an operation per image/cell combination then you should be able to refresh each cell as the operation completes. Doing this along with prioritising the operations will give you an optimal solution.
If the NSOperations sound complex or you are put off by this - please do try it - it is a lot simpler than I might have made it sound.
Have you tried calling [cell setNeedsDisplay]?