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 Development

Reply
 
LinkBack Thread Tools Display Modes
Old 09-17-2011, 01:14 AM   #1 (permalink)
Registered Member
 
Join Date: May 2011
Posts: 5
bumblebee9 is on a distinguished road
Default Threads and autoreleasepool questions

Hey guys,

As I understand there are several ways to send tasks to be performed in threads. The most common ones that I use are:

1) performSelector:withObject:afterDelay:

2) performSelectorOnMainThread:withObject:waitUntilDo ne:

3) performSelectorInBackground:withObject:

4) [NSThread detachNewThreadSelector:toTarget:withObject:]

My first question is, what is the difference between 1) and 2), besides the obvious parameter differences? Are they actually both working in the Main thread (whose autorelease pool was automatically created in main.m)? I just read from someone's post on Stackoverflow that method 1) is actually working in a new thread, and so an autorelease pool should be created for its selector method. Is this correct? I've been using 1) a lot, mostly to take advantage of the delay parameter, but I've never created an autorelease pool for them. Nothing catastrophic has happened.


Next, 3) and 4) both perform tasks in a separate thread. I hear that UI stuff should never be done in these threads, but I'm confused as to what is strictly UI.
I was trying to write code to basically play a repeating loading animation while a tableview is launching modally from a navigationcontroller. The animation is then stopped in the viewDidLoad method of the tableview controller. Initially, I just stuck the code to start the animation above the lines of code that start the modal view. What happened was the animation was never played.
Code:
[[self loadingView] playAnimation];

SettingsViewController *menus = [[SettingsViewController alloc] initWithNibName:@"SettingsViewController" bundle:nil];

MyNavigationController *navController = [[MyNavigationController alloc] initWithRootViewController:menus];

[menus setParent:navController];
[navController setDelegate:self];
menus.mainViewController = self;
	
[self presentModalViewController:navController animated:YES];
[navController release];
[menus release];
I then tried the following, and it worked...

Code:
[NSThread detachNewThreadSelector:@selector(settingsOpeningThread) toTarget:self withObject:nil];
[[self loadingView] playAnimation];



- (void) settingsOpeningThread {

NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];

SettingsViewController *menus = [[SettingsViewController alloc] initWithNibName:@"SettingsViewController" bundle:nil];

MyNavigationController *navController = [[MyNavigationController alloc] initWithRootViewController:menus];

[menus setParent:navController];
[navController setDelegate:self];
menus.mainViewController = self;
	
[self presentModalViewController:navController animated:YES];
[navController release];
[menus release];

[apool release];

}

Animation keeps playing until the SettingsViewController view is fully launched. But does launching modal views like this count as "UI" and should be avoided? Also I am getting some weird memory leak errors in Instruments every time the modal view is launched. But it's from one of those "System Libraries", which I've been told is very difficult to debug. What might be going wrong here?


Sorry for the embarrassingly long post. Any help will be appreciated!
bumblebee9 is offline   Reply With Quote
Old 09-17-2011, 11:13 AM   #2 (permalink)
Cocoa Junkie
 
Duncan C's Avatar
 
Join Date: Dec 2008
Location: Northern Virginia
Posts: 6,003
Duncan C has a spectacular aura about
Default

Quote:
Originally Posted by bumblebee9 View Post
Hey guys,

As I understand there are several ways to send tasks to be performed in threads. The most common ones that I use are:

1) performSelector:withObject:afterDelay:

2) performSelectorOnMainThread:withObject:waitUntilDo ne:

3) performSelectorInBackground:withObject:

4) [NSThread detachNewThreadSelector:toTarget:withObject:]

My first question is, what is the difference between 1) and 2), besides the obvious parameter differences? Are they actually both working in the Main thread (whose autorelease pool was automatically created in main.m)? I just read from someone's post on Stackoverflow that method 1) is actually working in a new thread, and so an autorelease pool should be created for its selector method. Is this correct? I've been using 1) a lot, mostly to take advantage of the delay parameter, but I've never created an autorelease pool for them. Nothing catastrophic has happened.


Next, 3) and 4) both perform tasks in a separate thread. I hear that UI stuff should never be done in these threads, but I'm confused as to what is strictly UI.
I was trying to write code to basically play a repeating loading animation while a tableview is launching modally from a navigationcontroller. The animation is then stopped in the viewDidLoad method of the tableview controller. Initially, I just stuck the code to start the animation above the lines of code that start the modal view. What happened was the animation was never played.
Code:
[[self loadingView] playAnimation];

SettingsViewController *menus = [[SettingsViewController alloc] initWithNibName:@"SettingsViewController" bundle:nil];

MyNavigationController *navController = [[MyNavigationController alloc] initWithRootViewController:menus];

[menus setParent:navController];
[navController setDelegate:self];
menus.mainViewController = self;
	
[self presentModalViewController:navController animated:YES];
[navController release];
[menus release];
I then tried the following, and it worked...

Code:
[NSThread detachNewThreadSelector:@selector(settingsOpeningThread) toTarget:self withObject:nil];
[[self loadingView] playAnimation];



- (void) settingsOpeningThread {

NSAutoreleasePool *apool = [[NSAutoreleasePool alloc] init];

SettingsViewController *menus = [[SettingsViewController alloc] initWithNibName:@"SettingsViewController" bundle:nil];

MyNavigationController *navController = [[MyNavigationController alloc] initWithRootViewController:menus];

[menus setParent:navController];
[navController setDelegate:self];
menus.mainViewController = self;
	
[self presentModalViewController:navController animated:YES];
[navController release];
[menus release];

[apool release];

}

Animation keeps playing until the SettingsViewController view is fully launched. But does launching modal views like this count as "UI" and should be avoided? Also I am getting some weird memory leak errors in Instruments every time the modal view is launched. But it's from one of those "System Libraries", which I've been told is very difficult to debug. What might be going wrong here?


Sorry for the embarrassingly long post. Any help will be appreciated!
Whoo boy. Long post. Lots of questions.


performSelector:withObject:afterDelay: does not spawn a new thread. Whoever said that is wrong. The second method, performSelectorOnMainThread:withObject:waitUntilDo ne, is intended to be used from another thread to send messages to the main thread. If you run it from the main thread it would probably have the same effect as performSelector:withObject. Since the main thread is already running, you will not need to create an autorelease pool.

The last 2 create a new thread and execute the specified selector on that thread. The selector you invoke in these methods should create an autorelease pool on entry to the method, and drain the pool on exit.

As to your question about UI: You should not do anything that causes a change to the screen except from the main thread. Pushing a modal view controller does absolutely count as UI. You should not do that from a separate thread. That code may have bad, unexpected results. You're probably getting memory leaks because you are doing UI from a separate thread. Specifically, you are triggering an animation, which runs after your code returns. Any autorelease calls the system makes in performing that animation will leak.

Core animation is managed by the system, and once it starts, it will run even if you are doing something time-consuming from the main thread.

Here's what to do:


1. Set up a separate method to present your modal view controller/table view. Let's call it doModalTableView.

2. Create a repeating core animation and make the call to start it running.

3. Call your doModalTableView method using performSelectorWithObject:afterDelay: and pass in a delay value of 0. That method returns immediately, and schedules the selector to be called on the next pass through the event loop. (even if the delay value is 0) That way, the animation you started in step 2 actually begins running. (UI changes don't actually take place until you return and visit the event loop)


As a follow-on to this discussion, you should really avoid creating threads manually. Creating a thread is an expensive operation, and ties up physical memory that can't be freed until the thread terminates.

Instead of managing threads yourself, you should create an NSOperationQueue, and add an NSOperation to the queue. That will ask the system to run the operation on a separate thread, but the system will take care of the housekeeping of creating a new thread if it needs to. The system can manage thread creation itself, and do it more efficiently than you can.

iOS 4.0 and later supports Grand Central Dispatch, (GCD), a framework that's been available on the Mac since OS 10.6, and is the most efficient way of handling concurrency. Using GCD does restrict you to iOS 4.0 and later, however. For greatest compatibility, you might want to use NSOperationQueue and NSOperation objects instead. Under iOS 4.x, the system uses GCD internally to implement NSOperations, so you get the performance gain for free on those platforms. NSOperation and NSOperationQueue are also simpler and easier to use than GCD.
__________________
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
Old 09-17-2011, 04:02 PM   #3 (permalink)
Registered Member
 
Join Date: May 2011
Posts: 5
bumblebee9 is on a distinguished road
Default

Quote:
Originally Posted by Duncan C View Post
Whoo boy. Long post. Lots of questions.


performSelector:withObject:afterDelay: does not spawn a new thread. Whoever said that is wrong. The second method, performSelectorOnMainThread:withObject:waitUntilDo ne, is intended to be used from another thread to send messages to the main thread. If you run it from the main thread it would probably have the same effect as performSelector:withObject. Since the main thread is already running, you will not need to create an autorelease pool.

The last 2 create a new thread and execute the specified selector on that thread. The selector you invoke in these methods should create an autorelease pool on entry to the method, and drain the pool on exit.

As to your question about UI: You should not do anything that causes a change to the screen except from the main thread. Pushing a modal view controller does absolutely count as UI. You should not do that from a separate thread. That code may have bad, unexpected results. You're probably getting memory leaks because you are doing UI from a separate thread. Specifically, you are triggering an animation, which runs after your code returns. Any autorelease calls the system makes in performing that animation will leak.

Core animation is managed by the system, and once it starts, it will run even if you are doing something time-consuming from the main thread.

Here's what to do:


1. Set up a separate method to present your modal view controller/table view. Let's call it doModalTableView.

2. Create a repeating core animation and make the call to start it running.

3. Call your doModalTableView method using performSelectorWithObject:afterDelay: and pass in a delay value of 0. That method returns immediately, and schedules the selector to be called on the next pass through the event loop. (even if the delay value is 0) That way, the animation you started in step 2 actually begins running. (UI changes don't actually take place until you return and visit the event loop)


As a follow-on to this discussion, you should really avoid creating threads manually. Creating a thread is an expensive operation, and ties up physical memory that can't be freed until the thread terminates.

Instead of managing threads yourself, you should create an NSOperationQueue, and add an NSOperation to the queue. That will ask the system to run the operation on a separate thread, but the system will take care of the housekeeping of creating a new thread if it needs to. The system can manage thread creation itself, and do it more efficiently than you can.

iOS 4.0 and later supports Grand Central Dispatch, (GCD), a framework that's been available on the Mac since OS 10.6, and is the most efficient way of handling concurrency. Using GCD does restrict you to iOS 4.0 and later, however. For greatest compatibility, you might want to use NSOperationQueue and NSOperation objects instead. Under iOS 4.x, the system uses GCD internally to implement NSOperations, so you get the performance gain for free on those platforms. NSOperation and NSOperationQueue are also simpler and easier to use than GCD.

Wow, great answers Duncan! Thanks a whole bunch! I will try out that alternative method.
I do try to stay away from threading, but that problem caused me quite a bit of headache so I had to resort to threads, albeit "doing it wrong". The method you proposed isn't running anything on a separate thread? If that works, that's great!
I've touched on GCD a bit before, but at the time thought the methods I listed above to be more straightforward so I used those instead.



Thank you!
bumblebee9 is offline   Reply With Quote
Old 09-17-2011, 04:42 PM   #4 (permalink)
Registered Member
 
Join Date: May 2011
Posts: 5
bumblebee9 is on a distinguished road
Default

Quote:
Originally Posted by bumblebee9 View Post
Wow, great answers Duncan! Thanks a whole bunch! I will try out that alternative method.
I do try to stay away from threading, but that problem caused me quite a bit of headache so I had to resort to threads, albeit "doing it wrong". The method you proposed isn't running anything on a separate thread? If that works, that's great!
I've touched on GCD a bit before, but at the time thought the methods I listed above to be more straightforward so I used those instead.



Thank you!

Hey Duncan I just tried your method and it seems to be working, except that my animation doesn't animate. It appears on screen with the first frame but doesn't actually start animating. The reason that I wanted to put a loading animation in the first place is that the things that need to be loaded into the modally launched tableview can grow once user puts more stuff in it. So overtime it'll take longer for the table to pop up. We needed something to let the user know it's loading.
The ios UIActivityIndicatorView is often used for things like these (which also didn't have that threading/timing problem interestingly), but we wanted our own look and feel.
So my "loading animation" is actually just UIImageView that keeps swapping image frames, controlled by a timer.
With the new method you suggested, I don't see the animation animate. I guess the old (wrong) method I had allowed the animation to start because it took longer for the new thread to actually start working. If I put a non-zero value for the delay with the new method, I get to see it animate for that long.
My intent was to play the animation while the tableview is trying to load modally. Does this make sense?
You mentioned Core Animation. I'm not sure what that is. But is it a better method?



Thank you.
bumblebee9 is offline   Reply With Quote
Old 09-18-2011, 03:33 PM   #5 (permalink)
Cocoa Junkie
 
Duncan C's Avatar
 
Join Date: Dec 2008
Location: Northern Virginia
Posts: 6,003
Duncan C has a spectacular aura about
Default

Quote:
Originally Posted by bumblebee9 View Post
Hey Duncan I just tried your method and it seems to be working, except that my animation doesn't animate. It appears on screen with the first frame but doesn't actually start animating. The reason that I wanted to put a loading animation in the first place is that the things that need to be loaded into the modally launched tableview can grow once user puts more stuff in it. So overtime it'll take longer for the table to pop up. We needed something to let the user know it's loading.
The ios UIActivityIndicatorView is often used for things like these (which also didn't have that threading/timing problem interestingly), but we wanted our own look and feel.
So my "loading animation" is actually just UIImageView that keeps swapping image frames, controlled by a timer.
With the new method you suggested, I don't see the animation animate. I guess the old (wrong) method I had allowed the animation to start because it took longer for the new thread to actually start working. If I put a non-zero value for the delay with the new method, I get to see it animate for that long.
My intent was to play the animation while the tableview is trying to load modally. Does this make sense?
You mentioned Core Animation. I'm not sure what that is. But is it a better method?



Thank you.
I assumed that you were using core animation to do your animation. Core Animation does magic that lets it run even when your code is doing a time-consuming synchronous task. Timers are a different story. Timers don't get called unless your code visits the event loop, so a manual timer-driven animation won't work.

Take a look at UIImageView. It has a property animationImages that lets you set up an array of images, and animationDuration, that specifies the duration of the animation, an animationRepeatCount that lets you control how many times the animation repeats. You can set those properties, then call startAnimating to start the image view animating. That should work where your timer-based animation will not. Use the approach I described, where you set up the animation, call startAnimating to start it animating, then call your time-consuming method using performSelector:WithObject:afterDelay:
__________________
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
Old 09-24-2011, 12:28 AM   #6 (permalink)
Registered Member
 
Join Date: May 2011
Posts: 5
bumblebee9 is on a distinguished road
Default

Quote:
Originally Posted by Duncan C View Post
I assumed that you were using core animation to do your animation. Core Animation does magic that lets it run even when your code is doing a time-consuming synchronous task. Timers are a different story. Timers don't get called unless your code visits the event loop, so a manual timer-driven animation won't work.

Take a look at UIImageView. It has a property animationImages that lets you set up an array of images, and animationDuration, that specifies the duration of the animation, an animationRepeatCount that lets you control how many times the animation repeats. You can set those properties, then call startAnimating to start the image view animating. That should work where your timer-based animation will not. Use the approach I described, where you set up the animation, call startAnimating to start it animating, then call your time-consuming method using performSelector:WithObject:afterDelay:

Hey thanks Duncan! That worked perfectly! I used UIImageView's own animations before, but kind of stayed away from it because I heard about its possible memory leaks in the animationImages property. Who knew it would work perfectly for this! Thanks, again!
bumblebee9 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 Off
Trackbacks are On
Pingbacks are On
Refbacks are On



» Advertisements
» Online Users: 405
16 members and 389 guests
7twenty7, Eclectic, eski, EvilElf, fiftysixty, HemiMG, iOS.Lover, JackReidy, jarv, pbart, Pudding, sacha1996, teebee74, UMAD, VinceYuan, yuncarl28
Most users ever online was 1,387, 04-10-2012 at 04:21 AM.
» Stats
Members: 175,672
Threads: 94,121
Posts: 402,905
Top Poster: BrianSlick (7,990)
Welcome to our newest member, yuncarl28
Powered by vBadvanced CMPS v3.1.0

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