I have an interface like this:
#interface AView : UIScrollView
{
UIView* m_view1;
UIView* m_view2;
...
}
-(void) method1;
-(void) method2;
...
#end
I need to access views from methods of the interface. I need to create, release, re-create them and also set properties.
The problem is, some methods of the interface are running in different threads. Since these methods access same views I have issues like one thread trying to re-create a view when another thread is trying to set some properties of a view being recreated.
How should I synchronize access to views?
First of all, do you know you can ONLY call the methods of the UIView class (and it's subclasses) in the main thread? But, if you are just doing create and release job in second thread, it's OK to do it.
Threading Considerations
Manipulations to your application’s user interface must occur on the
main thread. Thus, you should always call the methods of the UIView
class from code running in the main thread of your application. The
only time this may not be strictly necessary is when creating the view
object itself; but all other manipulations should occur on the main
thread.
In addition, you can use #synchronized() {object} to lock an object. But still, you can NOT call UIView's methods in second thread (in Objective-C even set property is calling method) even you've locked it.
Objective-C supports multithreading in applications. Therefore, two
threads can try to modify the same object at the same time, a
situation that can cause serious problems in a program. To protect
sections of code from being executed by more than one thread at a
time, Objective-C provides the #synchronized() directive.
The #synchronized()directive locks a section of code for use by a
single thread. Other threads are blocked until the thread exits the
protected code—that is, when execution continues past the last
statement in the #synchronized() block.
The #synchronized() directive takes as its only argument any
Objective-C object, including self.
You can use NSLock for example, but you shouldn't update your UI from other threads. The only thread from which you should update UI Is the main thread.
UIKit can only be used safely on the main thread! Do you use NSOperation?
You will see the usage ofperformSelectorOnMainThread:withObject:waitUntilDone: to move operations done from other threads to the main thread.
Related
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.
I know and heard most UI related shouldn't be done in thread other than main.
I also know that you can update non-view related data member(that you added) of UIView derived class.
I wonder if the below operations are fine or not to do in background thread.
allocing UIView
init UIView with/without(CGRectZero or just init) frame info
modifying frame/image(UIImageView's) property of UIView
modifying image property of NSObject derived class. (treating UIImage as data)
accessing subviews with subviews method
etc.. Is there a well defined documentation on this issue?
Thank you
You shouldn't be doing anything view related in a background thread. All of the items you listed should not be done in a background thread. If you're breaking your app up correctly for MVC, the view should only contain items that dictate how it is displayed. So anything relating to one should only be in the main thread.
All of your data manipulation should be residing in your model. It can be threaded as needed for performance. Just be careful that you send any messages to update the UI for the data manipulation on the main thread. This includes notifications. They gets sent on the same thread they were created on. So it's easy to forget to switch into mainThread when sending one.
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.
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.
Is it an error to call dealloc on a UIViewController from a background thread? It seems that UITextView (can?) eventually call _WebTryThreadLock which results in:
bool _WebTryThreadLock(bool): Tried to obtain the web lock from a thread
other than the main thread or the web thread. This may be a result of calling
to UIKit from a secondary thread.
Background: I have a subclassed NSOperation that takes a selector and a target object to notify.
-(id)initWithTarget:(id)target {
if (self = [super init]) {
_target = [target retain];
}
return self;
}
-(void)dealloc {
[_target release];
[super dealloc];
}
If the UIViewController has already been dismissed when the NSOperation gets around to running, then the call to release triggers it's dealloc on a background thread.
Yes, it is an error to make a UIViewController releasing in a background thread (or queue). In UIKit, dealloc is not thread safe. This is explicitly described in Apple's TN2109 doc:
When a secondary thread retains the target object, you have to ensure that the thread releases that reference before the main thread releases its last reference to the object. If you don't do this, the last reference to the object is released by the secondary thread, which means that the object's -dealloc method runs on that secondary thread. This is problematic if the object's -dealloc method does things that are not safe to do on a secondary thread, something that's common for UIKit objects like a view controller.
However, it's quite hard to respect this rule and if you put precondition(Thread.isMainThread) in all your view controller dealloc/deinit(), you probably would notice very weird (and hard to fix) cases in which this rule is not respected.
It seems that Apple is aware of this fragility and is moving away from this rule. In fact, when you annotate a class with #MainActor, everything is ensured to be executed in the Main Thread, except deinit(). In Swift 6, the compiler prevents you from calling main actor code from deinit().
Even if in the past we have been asked to ensure deallocation in the main thread, in the future we will be asked to ensure that deallocation can happen in any thread.
The simple rule is that it's an error to do anything on a UI* from a background thread.
The second post here had useful info, but their answer didn't work for me.
UIWebView in multithread ViewController
It also seems that this may have been addressed in iPhone OS 4, but not sure.
I ended up not releasing my UIWebView in the controller's dealloc when [NSThread isMainThread] was NO. Would rather leak than crash (until I get a better solution).
It is an error to call dealloc on anything at any time. You should only ever call release.
You should not access any UI related instances from a background thread. This includes using getter methods because they may modify things internally. However, retain and release are thread safe for any object at any time, as long as the normal rules for retain and release are followed. UI related instances include any object that is referenced by an active UIView or UIViewController.
performSelectorOnMainThread does not do anything more than retain an object until it gets to the main thread. It is safe to call on any UI related object.