Advertise Mobile SDKs Books Events Forum News Social Networking Support Us
Follow @iphonedevsdk on Twitter

Interface 2, Advanced iOS
Mockup & Code Gen
($9.99)

Make your own iPhone apps
and run them live!
(free)

Pic Frame Dynamo: Photo Editing
($0.99)

Abiliator
($1.99)

Want your application or service advertised on iPhone Dev SDK?

Go Back   iPhone Dev SDK Forum > iPhone SDK Development Forums > iPhone SDK Tutorials

Reply
 
LinkBack Thread Tools Display Modes
Old 05-09-2009, 09:47 AM   #1 (permalink)
Tutorial Author
 
Join Date: Jan 2009
Posts: 144
meowmix23F is on a distinguished road
Default [medium | advanced] Threading

How many times have you bumped your head on your desk for (one or more) of the following problems?

1. I used

[myactivityindicator startAnimating];
NSString *string = [[NSString alloc] initWithContentsOfURL......
[myActivityIndicator stopAnimating];

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:
Code:
- (IBAction)pressed:(id)sender;
- (void)entryPoint;
- (void)done:(NSString *)status;
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];
}
Ok, now a few explanations:
Code:
[self performSelectorOnMainThread:@selector(done:) withObject:str waitUntilDone:NO];
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
Ok, now the done: method

Code:
- (void)done:(NSString *)result
{
[act stopAnimating];
NSLog(@"thread finished loading variable");
UIAlertView *a = [[UIAlertView alloc] initWithTitle@"Message" message:[NSString stringWithFormat:@"loaded variable:\n%@",result] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
[a show];
[a release];
}
Basically, we make our spinner stop, Log that we loaded the variable, and then shoot out an AlertView saying, "Hey, its done! Here's what it is.."

Now, here's a few challenges:

1. Write a PHP program that uses mysql. Format:

http://www.mywebsite.com/ex.php?name=bob

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:

[NSString stringWithFormat:@"http://www.mywebsite.com/ex.php?name=%@",namefield.text];

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.
meowmix23F is offline   Reply With Quote
Old 05-09-2009, 06:28 PM   #2 (permalink)
Beast Mode
 
Join Date: Dec 2008
Age: 21
Posts: 1,971
Bertrand21 is on a distinguished road
Thumbs up Great Job!!!!

this is exactly what im looking for! Cant wait t0 see the rest!
__________________
Haters gonna Hate
Likers gonna Like
Bertrand21 is offline   Reply With Quote
Old 05-09-2009, 09:18 PM   #3 (permalink)
Former NeXTStep Developer
 
Join Date: Mar 2009
Posts: 997
FlyingDiver will become famous soon enough
Default

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
FlyingDiver is offline   Reply With Quote
Old 05-09-2009, 10:23 PM   #4 (permalink)
Tutorial Author
 
Join Date: Jan 2009
Posts: 144
meowmix23F is on a distinguished road
Default

Quote:
Originally Posted by FlyingDiver View Post
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
meowmix23F is offline   Reply With Quote
Old 05-09-2009, 10:44 PM   #5 (permalink)
Former NeXTStep Developer
 
Join Date: Mar 2009
Posts: 997
FlyingDiver will become famous soon enough
Default

Quote:
Originally Posted by meowmix23F View Post
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.

joe
FlyingDiver is offline   Reply With Quote
Old 05-09-2009, 10:49 PM   #6 (permalink)
Tutorial Author
 
Join Date: Jan 2009
Posts: 144
meowmix23F is on a distinguished road
Default

Quote:
Originally Posted by FlyingDiver View Post
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.

joe
Of course, everyone is entitled to program how they want. I just thought i'd share some of my knowledge with the community.
meowmix23F is offline   Reply With Quote
Old 10-01-2009, 05:48 PM   #7 (permalink)
Registered Member
 
Join Date: Jul 2008
Posts: 60
japaternoster is on a distinguished road
Default

Good little tutorial, but a correction:

It is not NSAutoReleasePool, but NSAutoreleasePool. Otherwise it won't compile.

Thanks
japaternoster is offline   Reply With Quote
Old 03-30-2011, 08:29 PM   #8 (permalink)
Cocoa Junkie
 
Duncan C's Avatar
 
Join Date: Dec 2008
Location: Northern Virginia
Posts: 6,005
Duncan C has a spectacular aura about
Default

Quote:
Originally Posted by meowmix23F View Post
How many times have you bumped your head on your desk for (one or more) of the following problems?

1. I used

[myactivityindicator startAnimating];
NSString *string = [[NSString alloc] initWithContentsOfURL......
[myActivityIndicator stopAnimating];

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:
Code:
- (IBAction)pressed:(id)sender;
- (void)entryPoint;
- (void)done:(NSString *)status;
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];
}
Ok, now a few explanations:
Code:
[self performSelectorOnMainThread:@selector(done:) withObject:str waitUntilDone:NO];
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
Ok, now the done: method

Code:
- (void)done:(NSString *)result
{
[act stopAnimating];
NSLog(@"thread finished loading variable");
UIAlertView *a = [[UIAlertView alloc] initWithTitle@"Message" message:[NSString stringWithFormat:@"loaded variable:\n%@",result] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
[a show];
[a release];
}
Basically, we make our spinner stop, Log that we loaded the variable, and then shoot out an AlertView saying, "Hey, its done! Here's what it is.."

Now, here's a few challenges:

1. Write a PHP program that uses mysql. Format:

http://www.mywebsite.com/ex.php?name=bob

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:

[NSString stringWithFormat:@"http://www.mywebsite.com/ex.php?name=%@",namefield.text];

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.
__________________
Regards,

Duncan C
WareTo

Check out our apps in the Apple App store


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.

See this tutorial on using UIView animations and layer animations:

See this thread on generating random, non-repeating text

Check out a very cool Macintosh Kaleidoscopes app called ScopeWorks that we released to the Mac App store.
Duncan C is offline   Reply With Quote
Reply

Bookmarks

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is On
Trackbacks are On
Pingbacks are On
Refbacks are On



» Advertisements
» Online Users: 466
17 members and 449 guests
alexeir, David-T, Dj_kades, Elad, foslock, HemiMG, iAppDeveloper, jeroenkeij, LunarMoon, Mijator, Pauluz85, pipposanta, QuantumDoja, robsmy, sacha1996, smithdale87, usernametaken
Most users ever online was 1,387, 04-10-2012 at 04:21 AM.
» Stats
Members: 175,679
Threads: 94,129
Posts: 402,928
Top Poster: BrianSlick (7,990)
Welcome to our newest member, xzoonxoom
Powered by vBadvanced CMPS v3.1.0

All times are GMT -5. The time now is 09:21 AM.
Powered by vBulletin® Version 3.8.0
Copyright ©2000 - 2012, Jelsoft Enterprises Ltd.
Search Engine Friendly URLs by vBSEO 3.3.0