Warning: UIKit should not be called from a secondary thread - iphone

In my iPhone app I need to connect to a web server as this can take some time I'm using threads like this:
[NSThread detachNewThreadSelector:#selector(sendStuff) toTarget:self withObject:nil];
- (void)sendStuff {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//Need to get the string from the textField to send to server
NSString *myString = self.textField.text;
//Do some stuff here, connect to web server etc..
[pool release];
}
On the row where I use self.textField I get a warning in console saying:
void _WebThreadLockFromAnyThread(bool), 0x5d306b0: Obtaining the web lock from a thread other than the main thread or the web thread. UIKit should not be called from a secondary thread.
How can I use the textField without getting this error?

It depends a little bit on what you want to do with the textField. If reading the value is the only thing, you can do:
[NSThread detachNewThreadSelector:#selector(sendStuff) toTarget:self withObject:self.textField.text];
- (void)sendStuff:(NSString*)myString {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//Do someting with myString
[pool release];
}
If you want to change a value on the textField you could:
[self.textField performSelectorOnMainThread:#selector(setText:) withObject:#"new Text"];

Perform any selectors that handle UI updates on the main thread. You can do this with the NSObject method -performSelectorOnMainThread:withObject:waitUntilDone:

Why not:
[NSThread detachNewThreadSelector: #selector(sendStuff:) toTarget: self withObject: self.textField.text];
?

This is indeed unsafe behaviour. The MainThread is the only one that should interface the UI. Have your thread return for instance a string to the mainthread and have a method there update the UI. You can do for instance do this by passing a selector to the other thread method, and then have the other thread call the selector on the mainthread.

[self performSelectorOnMainThread:#selector(myMethod) withObject:nil waitUntilDone:NO];
will work

Related

Strange, IPhone performSelector on sub-thread doesn't work.

I want to do some action on a thread using the following API, so strange selector poiOneBoxSearch hasn't been invoked, why? Any mistake on the code? Thanks.
- (void)poiOneBoxSearch{
[self poiOneBoxSearcWithQueryString:#"coffee" isFinished:YES];
}
- (void)test1{
NSThread* thread = [[NSThread alloc] init];
[self performSelector:#selector(poiOneBoxSearch)
onThread:thread
withObject:nil
waitUntilDone:YES];
[thread release];
}
If You want use performSelector Method You should Read below Link
,I Think You missed SOmething
Please Goes Through This Link
If Not you may Use Below Code.
Try This
- (void)test1{
[NSThread detachNewThreadSelector:#selector(poiOneBoxSearch) toTarget:self withObject:nil];
}
Try this:
[self performSelectorInBackground:#selector(poiOneBoxSearch) withObject:nil waitUntilDone:YES];
[self performSelectorInBackground:#selector(poiOneBoxSearch) withObject:nil];
- (void) poiOneBoxSearch{
#autoreleasepool {
[self poiOneBoxSearcWithQueryString:#"coffee" isFinished:YES];
} }
The most important thing that you have to keep in mind is that since this method creates a thread on the given selector, the selector must have an autorelease pool just like any other thread in a reference-counted memory environment.

How to cancel or stop NSThread?

I'm doing an app that loads the contents of viewControllers using NSThread while is reading an XML file.
I have it done as follows:
-(void)viewDidAppear:(BOOL)animated
{
// Some code...
[NSThread detachNewThreadSelector:#selector(loadXML) toTarget:self withObject:nil];
[super viewDidAppear:YES];
}
-(void)loadXML{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Read XML, create objects...
[pool release];
}
My problem is that I don't know how to stop the NSThread if the user changes to another viewController while the NSThread is loading, doing that the app crashes.
I've tried to cancel or exit the NSThread as follows but without success:
-(void)viewsDidDisappear:(BOOL)animated{
[NSThread cancel];
// or [NSThread exit];
[super viewDidDisappear:YES];
}
Can anyone help? Thanks.
When you detach new thread, you can no more cancel or exit it from viewDidDisappear etc. These UI specific methods execute only on main thread so the exit/cancel applies to the main thread which is obviously wrong.
Instead of using the detach new thread method, declare NSThread variable in .h and initialize it using initWithTarget: selector: object: method and cancel it whenever/wherever you want to..
you can also use [NSThread exit]; method of NSThread.
It's better to let a thread end gracefully, i.e. reach its natural conclusion, if you can. It sounds like in your case you can afford to. Also be sure that you're updating the user interface from the main thread, not a secondary thread, as UIKit is not thread safe.
You wrote:
... the app stops responding while the thread finishes...
Once you flag a thread for cancelling or exit, you have to manually stop whatever the thread was called to do. An example:
....
- (void) doCalculation{
/* Do your calculation here */
}
- (void) calculationThreadEntry{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSUInteger counter = 0;
while ([[NSThread currentThread] isCancelled] == NO){
[self doCalculation];
counter++;
if (counter >= 1000){ break;
} }
[pool release]; }
application:(UIApplication *)application
- (BOOL)
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
/* Start the thread */
[NSThread detachNewThreadSelector:#selector(calculationThreadEntry)
toTarget:self withObject:nil];
// Override point for customization after application launch. [self.window makeKeyAndVisible];
return YES;
}
In this example, the loop is conditioned on the thread being in a non-cancelled state.

How to set up an autorelease pool when using [NSThread detachNewThreadSelector:toTarget:withObject:]

Hi I'm usuing [NSThread detachNewThreadSelector:toTarget:withObject:] and I'm getting a lot of memory leaks because I have no autorelease pool set up for the detached thread. I'm just wondering where I actualy do this? Is it before I call
[NSThread detachNewThreadSelector:toTarget:withObject:]
or in the method that is being ran in the other thread?
Any help would be appreciated, some sample code would be great.
Thanks.
in the method you call with the thread... i.e. given this...
[NSThread detachNewThreadSelector:#selector(doStuff) toTarget:self withObject:nil];
Your method would be...
- (void)doStuff {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//Do stuff
[pool release];
}
You have to set up an autorelease pool in the method you call and that will be executed in the new detached thread.
For example:
// Create a new thread, to execute the method myMethod
[NSThread detachNewThreadSelector:#selector(myMethod) toTarget:self withObject:nil];
and
- (void) myMethod {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Here, add your own code
// ...
[pool drain];
}
Note that we use drain and not release on the autoreleasepool. On iOS, it has no difference. On Mac OS X, if your app is garbage collected, it will triggers garbage collection. This allows you to write code that you can re-use more easily.
Create the new thread:
[NSThread detachNewThreadSelector:#selector(myMethod) toTarget:self withObject:nil];
Create the method that is called by the new thread.
- (void)myMethod
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Your Code
[pool release];
}
What if you need to do something to the main thread from inside your new thread (for example, show a loading symbol)? Use performSelectorOnMainThread.
[self performSelectorOnMainThread:#selector(myMethod) withObject:nil waitUntilDone:false];
Refer :- iPhone SDK Examples
Do it in the method you call. Essentially, you should set up the method that gets called as a self-contained work unit (in fact, it will then be compatible with being called through either [NSOperation][1] or Grand Central Dispatch, too: both better ways of organising concurrent work).
But what if I can't change the implementation of the method I'm calling on a new thread?
In that case, you would go from doing this:
[NSThread detachNewThreadSelector: #selector(blah:) toTarget: obj withObject: arg]
to doing this:
[NSThread detachNewThreadSelector: #selector(invokeBlah:) toTarget: self withObject: dict]
- (void)invokeBlah: (id)dict {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [dict objectForKey: #"target"];
id arg = [dict objectForKey: #"argument"];
[obj blah: arg];
[pool release];
}
rather than using the dictionary, you could also create an NSInvocation that encapsulates the remote object call. I just chose a dictionary because it's the quickest way to show the situation in a SO answer. Either would work.
The documentation states that the method run in the thread must create and destroy its own autorelease pool. So if your code has
[NSThread detachNewThreadSelector:#selector(doThings) toTarget:self withObject:nil];
The method should be
- (void)doThings {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//Do your things here
[pool release];
}

Call delegate method from a thread

I've this bit of code:
- (IBAction)registerAction:(id)sender {
[NSThread detachNewThreadSelector:#selector(registerThread) toTarget:self withObject:nil];
}
- (void)registerThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
MyDelegate *delegate = (MyDelegate *)[[UIApplication sharedApplication] delegate];
NSInteger locationID = [delegate.api checkCoordinate:[NSString stringWithFormat:#"%f,%f",
location.coordinate.latitude, location.coordinate.longitude]];
NSNumber *status = [api registerWithUsername:usernameField.text
password:passwordField.text email:emailField.text andLocation:locationID];
[self performSelectorOnMainThread:#selector(registrationDoneWithStatus:) withObject:[NSNumber numberWithInt:1] waitUntilDone:NO];
[pool release];
}
it works nicely, but sometimes I get this error:
void _WebThreadLockFromAnyThread(bool), 0x6157e30: Obtaining the web lock from a thread other than the main thread or the web thread. UIKit should not be called from a secondary thread.
And it seems that only using the delegate I get this error, and I don't know how to resolve.
Thanks in advance :)
I've run into the same problem recently.
There may be some active views (eg. UITextField,UITextView). Try resignFirstResponder those views before accessing delegate
You fix the problem by very carefully thinking through your application's concurrency architecture and ensuring that you aren't exercising anything from a thread that should only be done on the main thread.
In this case, you are causing the UIKit to execute code from a secondary thread. If you were to set a breakpoint on _WebThreadLockFromAnyThread, you would know exactly where.
It is exceedingly atypical to use the app's delegate from a secondary thread in anything but the most extremely controlled circumstances.
tl;dr You can't make an app threaded by detaching a new thread against a random selector.

iPhone app freezes when parsing XML

My app freezes whenever I parse an XML feed.
I have tried calling this instead:
[NSThread detachNewThreadSelector:#selector(parseXML) toTarget:self withObject:nil];
which calls:
-(void) parseXML {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self parseXMLFileAtURL:path];
[pool drain];
}
but my app has become quite unstable as a result...the iPhone Simulator just crashed with no error warning.
Rather than calling :
[NSThread detachNewThreadSelector:#selector(parseXML) toTarget:self withObject:nil];
you should call :
[self performSelectorInBackground:#selector(parseXML) withObject:nil]
Your UI is freezing because you are doing lengthy operations in UI Thread.