and the activity indicator never started + stopped spinning!
2. I tried to move a large file and use activity indicators, but It wouldn't animate.
Well, thanks to the beauty of threads there *is* a solution to your problem.
You see, when your app starts up, you are in something called the "main thread." All UIKit calls (and CF** SC**) are from this thread. Meaning, if you calling an NSString class function, then its not going to go on to animating the activity indicator, its just going to hang there and wait for the NSString operation to finish. Annoying, right? But what if we could have our own main thread, but just for our selfish needs? Maybe we want to grab the contents of a URL, and then display it, while keeping the user updated as to what exactly we are doing? Well, here's the solution. We create our own thread, do as we please, and then tell the main thread that we're done, so that it can handle accordingly.
So, to begin:
Go into your header file (for your view controller), and add the method definitions for these methods:
Also, create an activity indicator (nonatomic, retain atr.) as an IBOutlet, along with a UILabel thats an IBOutlet. Name the Acitvity Indicator "act", and name the label "label". Now, go into the view controller nib and set up these outlets (also create a button, set up the "touch up inside" event for the button to - (IBAction)pressed..) . For the purpose of this tutorial, we're going to be loading a huge text file into a variable, so that we can manipulate that variable. So, create a text file, and write "0" in it about, i don't know, a few hundred times (write a row, and then copy + paste that row). Now, drag this file into our xcode project, (make sure it's named "file.txt"). Ok, now go into your implementation file. Here's where most of the interesting stuff is. add this method:
Code:
- (IBAction)pressed:(id)sender
{
// now, we create the thread
[NSThread detachNewThreadSelector:@selector(entryPoint) toTarget:self withObject:nil];
[act startAnimating];
}
- (void)entryPoint
{
// the new thread will be spawned here. We have to set up something first
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
NSString *str = [[[NSString alloc] initWithContentsOfFile:@"file.txt"] autorelease];
[self performSelectorOnMainThread:@selector(done:) withObject:str waitUntilDone:NO];
[pool release];
}
this is saying that we should perform the selector "done" on the main thread, and pass the variable "str" as a parameter to it (done: takes an NSString * parameter). WaitUntilDone: specifies whether or not our thread should wait for the function to return or for it to proceed while the task is pending.
Also, when you create a thread, you MUST create an autorelease pool
EX:
Code:
// create an autorelease pool
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
// all code for thread goes here...
[pool release];
// release the pool, so we don't leak memory
create a mysql table called "people".
Add a column for name, and one for age.
The PHP program will traverse the table, looking for a person named the variable $_GET['name'] which is equivalent to the ?name= part.
Have the PHP program output the age if found, or "NO"
On your iphone program, accept input for a name. Start a thread which will use a formatted NSString with the input in the url:
Use NSString contents of URL instance || class function to grab the contents, and use a similar done: method to process them. Wrap this all up with an activity indicator that starts and stops, and you'll be well on your way with threads.
- meowmix
Last edited by meowmix23F; 05-09-2009 at 10:32 PM.
You can also do this without creating a new thread by using an NSTimer to delay the start of the processing method until after the activity indicator has been started in the main loop. There's other ways to do a delayed message invocation as well.
You can also do this without creating a new thread by using an NSTimer to delay the start of the processing method until after the activity indicator has been started in the main loop. There's other ways to do a delayed message invocation as well.
joe
That's true, but i find that threads are much more useful for long tasks, and are easier to work with. Also, the point of this tutorial was to explain threads, not timers
That's true, but i find that threads are much more useful for long tasks, and are easier to work with. Also, the point of this tutorial was to explain threads, not timers
Fair enough. I just think it would have been good to point out that there's several ways to do this, threads being one of them.
and the activity indicator never started + stopped spinning!
2. I tried to move a large file and use activity indicators, but It wouldn't animate.
Well, thanks to the beauty of threads there *is* a solution to your problem.
You see, when your app starts up, you are in something called the "main thread." All UIKit calls (and CF** SC**) are from this thread. Meaning, if you calling an NSString class function, then its not going to go on to animating the activity indicator, its just going to hang there and wait for the NSString operation to finish. Annoying, right? But what if we could have our own main thread, but just for our selfish needs? Maybe we want to grab the contents of a URL, and then display it, while keeping the user updated as to what exactly we are doing? Well, here's the solution. We create our own thread, do as we please, and then tell the main thread that we're done, so that it can handle accordingly.
So, to begin:
Go into your header file (for your view controller), and add the method definitions for these methods:
Also, create an activity indicator (nonatomic, retain atr.) as an IBOutlet, along with a UILabel thats an IBOutlet. Name the Acitvity Indicator "act", and name the label "label". Now, go into the view controller nib and set up these outlets (also create a button, set up the "touch up inside" event for the button to - (IBAction)pressed..) . For the purpose of this tutorial, we're going to be loading a huge text file into a variable, so that we can manipulate that variable. So, create a text file, and write "0" in it about, i don't know, a few hundred times (write a row, and then copy + paste that row). Now, drag this file into our xcode project, (make sure it's named "file.txt"). Ok, now go into your implementation file. Here's where most of the interesting stuff is. add this method:
Code:
- (IBAction)pressed:(id)sender
{
// now, we create the thread
[NSThread detachNewThreadSelector:@selector(entryPoint) toTarget:self withObject:nil];
[act startAnimating];
}
- (void)entryPoint
{
// the new thread will be spawned here. We have to set up something first
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
NSString *str = [[[NSString alloc] initWithContentsOfFile:@"file.txt"] autorelease];
[self performSelectorOnMainThread:@selector(done:) withObject:str waitUntilDone:NO];
[pool release];
}
this is saying that we should perform the selector "done" on the main thread, and pass the variable "str" as a parameter to it (done: takes an NSString * parameter). WaitUntilDone: specifies whether or not our thread should wait for the function to return or for it to proceed while the task is pending.
Also, when you create a thread, you MUST create an autorelease pool
EX:
Code:
// create an autorelease pool
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
// all code for thread goes here...
[pool release];
// release the pool, so we don't leak memory
create a mysql table called "people".
Add a column for name, and one for age.
The PHP program will traverse the table, looking for a person named the variable $_GET['name'] which is equivalent to the ?name= part.
Have the PHP program output the age if found, or "NO"
On your iphone program, accept input for a name. Start a thread which will use a formatted NSString with the input in the url:
Use NSString contents of URL instance || class function to grab the contents, and use a similar done: method to process them. Wrap this all up with an activity indicator that starts and stops, and you'll be well on your way with threads.
- meowmix
A few points:
This code does much more than just make it possible to start a progress indicator before beginning a time-consuming operation. As somebody else mentioned, there is a much simpler way to do THAT. This threaded approach moves the time-consuming operation to the background, so the UI can run at the same times.
There are some gotchas in running code on another thread. It works best if the code running on a separate thread does not share any variables (instance variables or globals) from other threads. If the worker thread needs to use variables from the main thread, it should really make a local copy. If it needs read/write access, things get much more complicated. The worker thread also must not do anything that changes the UI. If the worker thread wants to update a label, say, it needs to use performSelectorOnMainThread to do it.
The main thread really needs some way to terminate the worker thread(s) early. Otherwise, the only way to stop the thread is to quit the application. (OK, you can probably use low-level thread management code to terminate the thread, but that's an ugly, risky thing.) NSOperationQueue objects (below) have a built-in mechanism for sending a "nevermind, please terminate early" message to the jobs they run in a separate thread.
Starting a new thread is a fairly expensive operation. Apple introduced the NSOperation and NSOperationQueue objects to handle concurrency in a cleaner, more efficient manner. The idea is that you create an NSOperationQueue as a "job jar" to hold tasks that you want to run in the background. You then add jobs (NSOperation objects) to the "job jar" as needed, and the system runs them for you. These classes are well documented and easy to use. If memory serves, they've been available since the first version of iOS.
Check out this password generator app that shows various techniques including using a data container singleton object to share data between objects in your project.