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.
Related
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.
I want to display the view first and then load the data in a background thread. When I navigate from root controller to the view controller, I want to display the view first. As of now, it stays on the root controller until the view controller is loaded. Here's my code for the root controller.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ProductDetailViewController *tempProductDetail = [[ProductDetailViewController alloc] init];
[self.navigationController pushViewController:tempProductDetail animated:YES];
[tempProductDetail release];
}
ProductDetailViewController, here I want to display the view first and then load the data...
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
[self performSelectorOnMainThread:#selector(workerThread) withObject:nil waitUntilDone:NO];
}
-(void) workerThread{
NSAutoreleasePool *arPool = [[NSAutoreleasePool alloc] init];
[arPool release];
}
Don't know what I'm doing wrong. Please, help.
Use [self performSelectorInBackground:#selector(workerThread) withObject:nil]; instead of
[self performSelectorOnMainThread:#selector(workerThread) withObject:nil waitUntilDone:NO];
found the answer for this issue,
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:YES];
[self performSelectorInBackground:#selector(workerThread) withObject:nil];
}
- (void) workerThread
{
// Set up a pool for the background task.
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
// only do data fetching here, in my case from a webservice.
//...
// Always update the components back on the main UI thread.
[self performSelectorOnMainThread:#selector(displayView) withObject:nil waitUntilDone:YES];
[pool release];
}
// Called once the background thread task has finished.
- (void) displayView
{
//here in this method load all the UI components
}
Consider using the following pattern instead for threading, in my opinion it's much cleaner:
- (void)viewWillAppear:(BOOL)animated
{
NSInvocationOperation *operation = [[NSInvocationOperation alloc]
initWithTarget:self
selector:#selector(someFunction)
object:nil];
[[NSOperationQueue currentQueue] addObject:operation]; // this will actually start the thread
[operation release];
}
- (void)someFunction
{
// don't need to initialize and release an autorelease pool here,
// you can just write a function as usual ...
[self updateUI];
}
- (void)updateUI
{
if (![NSThread isMainThread]) // if we need a UI update, force it on main thread
{
[self performSelectorOnMainThread:#selector(updateUI) withObject:nil waitUntilDone:YES];
return;
}
// do UI updates here
}
By writing code in this way, you can more dynamically decide which function you want to thread, since there is no autorelease pool requirement. If you need to do UI updates, the updateUI function will make sure for itself that it's running on the main thread, so the caller doesn't need to take this into account.
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];
}
I want to run a method in a background thread, the first method will run another method on the same (background) thread after some seconds. I wrote this:
- (IBAction)lauch:(id)sender
{
[self performSelectorInBackground:#selector(first) withObject:nil];
}
-(void) second {
printf("second\n");
}
-(void) first {
NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];
printf("first\n");
[self performSelector:#selector(second) withObject:nil afterDelay:3];
printf("ok\n");
[apool release];
}
but the second method is never called, why? and, how may i accomplish my goal?
thanks
You have to have a running run loop for performSelector:withObject:afterDelay: to work.
Your code executes first and, when first exits, the thread is gone. You need to run a run loop.
Add:
[[NSRunLoop currentRunLoop] run];
To the end of first.
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