I've been trying to implement this for a long time and I have gotten no favorable results.
Say I have a method in the which an HTTP request is performed (specifically, a twitter update), and say I want to display a UIActivityIndicatorView while the HTTP request is in progress (I know when it is done because there are delegate methods that are called when the request is done, either with positive results or negative ones).
I've seen many answers that say that threading is necessary for the implementation of this class. At first I tried calling the startAnimating method in a different thread and the stopAnimating method directly (without starting a new thread). After that I saw how this guy does it and I thought this was safer as I was starting and stopping the indicator in two different methods (the delegate methods for the twitter update).
However, none of this two ways of doing this have given me the results I want (the activity indicator does not show up at all). Is there anything I'm missing?
Thank you in advance and I apologize if my question is too long.
Your help is very much appreciated.
Threading is absolutely forbidden when working with UIKit subclasses. You may have seen reports that UIActivityIndicatorView uses threading internally, but in no way does that mean you can access the object from multiple threads. All UIView subclasses (including UIActivityIndicatorView) must only be accessed from the main thread. This includes calling -startAnimating and -stopAnimating.
If you rewrite your code such that you're only ever accessing the activity view on the main thread, and it still isn't working, then I would guess that the view was either not added to a visible view, is covered up by another view, or has a frame that puts itself outside of the visible area of its superview.
You cannot perform UI stuff in a secondary thread.
You should perform your HTTP request in a secondary thread, while calling the activity view from the main thread.
I recommend using DSActivityView which is so much easier to use. Just 1 line to show an activity view, 1 line to hide.
MBProgressHUD it's also easy and shows and hides itself when the secondary thread has started/finished. Something like this:
[HUD showhileexecuting:"yourstuff" animated:YES]
"Yourstuff" will run on a separate thread.
You should not perform UI activities in a secondary thread.
Related
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.
Is there any significant difference in performance when you call
[someObject performSelector:#selector(testMethod:) withObject:anotherObject];
vs
[someObject testMethod:anotherObject];
?
The first causes an extra call to objc_msgSend() that isn't necessary in the second case.
The performance difference is unlikely to remotely matter unless you are calling said method as quickly as you possibly can many 10s of thousands of times and you aren't doing any significant work in testMethod:.
I.e. don't worry about it unless you measure an actual performance problem.
Interesting fact, performing a selector with a delay of 0 causes that method to be called at the top of the next run loop of the app. You can use that to delay certain events that occur frequently (used a lot in optimizations of UI, like images that get reloaded in a UIScrollView)
No there isn't any performance hit that I am aware of, and if there is any it is not significant.
I’ve come across an important difference when passing data to another view controller in prepareForSegue.
using:
[viewController performSelector:#selector(aMethod:) withObject:anObject];
aMethod is called AFTER viewDidLoad and viewWillAppear of the destination viewController.
using:
[viewController aMethod: anObject] ;
aMethod is called BEFORE viewDidLoad and viewWillAppear of the destination viewController.
So if you’re sending data important for the setup of the destination viewController, use the second way.
There is a lot difference in above both methods. I was trying to get animation of Two buttons coming from right side and stops at center but the second button was coming with 0.3 second delay. Now the main point comes here. I was using one animation method for both of 2 buttons. Now i wanted that when I click Finish button, then both buttons should go to left and again New buttons come. This was fine till reading.
Now when i was writing method for Finish button click. I was performing going out of buttons Animation first and then coming in buttons, but when I used the Above second method i.e. [someObject testMethod:anotherObject]; then what happens is I was not able to see the Going out Animation and directly coming in animation of buttons was shown.
Here actually comes the use of first method i.e. [someObject performSelector:#selector(testMethod:) withObject:anotherObject withDelay:delay];
The reason I found was when I click the Finish button the animation runs in different thread and the other code runs in different thread so the going out action was performed in another thread and coming in was performed in another thread. So first thread was not shown.
After using first method with Delay time of total animation. I achieved my goal. So both methods have their own significance.
For my experience,there are two differences:
The first one can add afterDelay:(CGFloat)seconds, and this is the only case I use the first one.
[someObject performSelector:#selector(testMethod:) withObject:anotherObject afterDelay:1.0];
The second one, you need to define it in someObject.h. Otherwise, you will get a compile warning.
The answer is that they are exactly the same.
There are two really good articles one from Mike Ash, where he explains the objc_msgSend():
http://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-build-objc_msgsend.html
And an another one from Tom Dalling who is explaining that perform selector is calling objc_msgSend().
http://tomdalling.com/blog/cocoa/why-performselector-is-more-dangerous-than-i-thought/
I am writing an app for the iPhone and arrived at this situation.
I have a view controller, myViewController, that will dealloc whenever the user taps the "back" button on the screen. There a thread in the background that communicates with a remote server and may message a method, updateUI, method in myViewController.
What would happen if the background thread messages updateUI in myViewController, but the user just happened to tapped the "back" button at the right time such that it causes myViewController to dealloc while updateUI is still executing?
My guess is that the dealloc method will run and the app might crash if updateUI ends up using a null pointer. Assuming this is the case, the current solution I have is:
[self retain];
// updateUI code here
[self release];
I am unsure if this is the best solution, as I feel that this is a common problem when dealing with multiple threads.
Is my assumption correct? If so, is there a better solution?
What you are describing is known as a "race condition." Race conditions can be difficult to identify in testing, track down once reported, and reproduce because sometimes execution in the debugger can effectively modify how the code is being executed (avoiding the condition that one is trying to reproduce). Race conditions are one of the major pitfalls in concurrent programming - making the area deceptively difficult to do well.
In principle, it is a best practice to minimize the use of shared resources and closely qualify how the sharing is coordinated when implementing concurrency. If an object is shared across multiple threads, it should be retained by each of them to ensure that the object stays in-scope while each thread completes its processing.
Apple has been taking steps to simplify implementing concurrency. This is a good starting point for familiarizing yourself with the topic on iOS.
http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html%23//apple_ref/doc/uid/TP40008091
It's also useful to be aware that Objective-C 2.0's properties can support atomic operations (and are atomic by default, thus the nonatomic keyword to disable this default).
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html
And, this is the old-school guide to threads (out of favor approach, but still useful background - be sure to be familiar with NSLock).
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html%23//apple_ref/doc/uid/10000057i
Whenever some part of your code depends on another, it should retain this dependency until it is not needed. In your case the background worker should retain the controller and only release it when the work is done (or cancelled).
If you dealloc and then nil your objects then this shouldn't be an issue - you can send messages to nil in objective-c.
Alternatively, if you wanted the viewController to get the message and if your targeting iOs 4, you can use blocks and GCD. Blocks auto retain objects and therefore if a block references your viewController, would keep it around for as long as its needed, even if -(void)dealloc ; has been called.
Here is a decent Block tutorial
Yes, your app will crash, likely with something along the lines of EXC_BAD_ACCESS.
As far as multithreading, you will want to retain your objects until everything is done with them and program defensively. Check for the existence of objects before trying to manipulate them.
I wrote some sample code in a separate application, that basically queues up items in an NSMutableArray. I then have a method which loops through that array and creates an NSInvocationOperation, puts it in the NSOperationQueue, releases the operation and so forth. The method then gets called (simply prints out the string that was stored in the array and passed into the operation), does it's job, and sends an NSNotification back to the ViewController. The observer method gets hit, but the operations have a lag on them. For instance, the observer method simply updates a UILabel with how many messages are left in the queue. It eventually does this, but there seems to be a five second lag in between all of the NSOperations completing and the UI updating. To me it seems like the NSOperationQueue is blocking the main thread. Is there anyway to get the UI to respond immediately to the notifications?
One important note is that I have not tested this on the phone yet, just the simulator. I'm not sure if this makes a difference.
Sorry in advance. I'm away from my computer and I don't have the code in front of me. Hopefully I explained it well enough. Also I have read the documentation, just haven't found anything that's really answering this specific question for me.
The delay is typical of UI updates that are performed on threads other than main.
To see your update instantly on the UILabel, be sure to invoke whatever method is updating the label's text as follows:
[self performSelectorOnMainThread:#(myMethodToUpdateLabelWithText:) withObject:text waitUntilDone:NO];
where myMethodToUpdateLabelWithText is a method within your class that sets the label's text value.
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.