I have the following dispatch queue my app :
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^ {
[activeModel freeUpMallocedData];
// UI Updates have to be made on the main thread, so request from GCD.
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^ {
[mainViewController removeTidyUpScreen];
[mainViewController showSceneList];
[activeModel release];
});
});
The freeUpMallocedData method updates a UI Progress View :
- (void) freeUpMallocedData {
// Calculate the percentage increase for each item in the pointerStorageArray as it is released so we can update the Progress Screen.
float arrayCount = [pointerStorageArray count];
float incrementCounter = 1 / arrayCount; // Caculates 1% of pointerStorageArray
float newValue = incrementCounter;
int stepCounter = 0;
NSString * message;
// Now iterate through the pointerStorageArray and free all malloced memory.
for (NSValue * value in pointerStorageArray) {
stepCounter ++;
newValue = newValue + incrementCounter;
message = [NSString stringWithFormat:#"Freeing Up Memory (%d of %d) ...", stepCounter, (int)arrayCount];
free(value);
[self tidyUpProgress:message amount:newValue];
}
}
The tidyUpProgress method then executes on the main thread.
- (void) tidyUpProgress: (NSString *) progressMsg amount: (float) amount {
if (tidyUpMonitorDelegate) {
tidyUpProgressMsg = progressMsg;
tidyUpProgressAmount = amount;
[tidyUpMonitorDelegate performSelectorOnMainThread:#selector(model3DTidyUpProgressUpdate) withObject:nil waitUntilDone:NO];
}
}
- (void) model3DTidyUpProgressUpdate {
progressView.progress = app.activeModel.tidyUpProgressAmount;
loadingStatus.text = app.activeModel.tidyUpProgressMsg;
}
The problem is that the app crashes when the freeUpMallocedData method completes. The reason for this is that my initial dispatch queue moves on to request the main queue which then releases activeView. This seems to hijack the thread from the tidyUpMonitorDelegate before it can perform its last update - when it gets the main thread back the activeView has been released and therefore the app crashes as the model3DTidyUpProgresUpdate method is requesting access to variable in a class which has now been dealloced.
Can anyone advise on how to fix this timing issue ?
Thank you in advance.
Just a thought - try renaming the variable inside the dispatch:
dispatch_queue_t mainqueue = dispatch_get_main_queue();
dispatch_async(mainqueue, ^ {
You use two different mechanisms to schedule tasks in the main thread : dispatch_asyc and performSelectorInMainThread:withObject:waitUntilDone:. Each mechanism uses its own queue, which is read by the main run loop.
The order in which those queue are read is undefined. Thus, a task scheduled by performSelectorInMainThread:withObject:waitUntilDone may be performed after (or before) a task scheduled with dispatch_async regardless of which was scheduled first.
You should update tidyUpProgress: to use dispatch_async. Then the order will be guaranteed.
Moreover, after releasing an object, you should always nullify the variable that holds the reference to that object.
this is wrong way:
float arrayCount = [pointerStorageArray count];
-correct way:
NSUinteger arrayCountInt = [pointerStorageArray count];
NSNumber *arrayCountNumber = [NSNumber numberWithUnsignedInteger:arrayCountInt]
float arrayCount = [arrayCountNumber floatValue];
Related
What if I have a function that returns an int and the return value of the int is taken from the block?
For example:
- (int) queryForKey:(NSString *)aKey view:(UIButton *)aView countView:(UIView *)aCountView counter:(int) count {
//some initialization
[query countObjectsInBackgroundWithBlock:^(int number, NSError * error) {
[aCountView addSubview:self.generateCountLabel];
if (number > 0){
[aView setUserInteractionEnabled:YES];
[aView setEnabled:YES];
}
//return number; //doing this will generate an error
}];
}
also another question is, what if I have an int passed in as an argument of the function above and I would like to assign a value to it. Is some thing like that even possible?
Well your block does not have a return value, it returns void.
To return a value you could use the __block modifier on a variable outside your block and store then answer there which can then be used by the rest of your method (or code).
The problem is that you have a synchronous method (one that wants to return the value immediately) that needs to return a value derived from an asynchronous method (one that goes about it's business in a different thread).
There are a couple of ways of fixing this:
wait for the countObjectsInBackgroundWithBlock: method to complete, use the __block pattern as #simonpie described.
replace the return number; with a call to something somewhere interested in the resulting number. This also means that queryForKey:view:countView: will likely return void.
The latter is the preferred solution as it will not block the thread calling the queryForKey:... method.
Note that you can't diddle UIKit classes on anything but the main thread. If that block is executed on a background thread, then doing what you are doing in the block is invalid.
I've found a better solution. Hopefully this will help someone else who stumbles across the question. I would implement a solution to your code like so:
- (int) queryForKey:(NSString *)aKey view:(UIButton *)aView countView:(UIView *)aCountView counter:(int) count {
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
__block int number;
//some initialization
[query countObjectsInBackgroundWithBlock:^(int number, NSError * error) {
dispatch_async(queue, ^{
[aCountView addSubview:self.generateCountLabel];
if (number > 0){
[aView setUserInteractionEnabled:YES];
[aView setEnabled:YES];
}
dispatch_semaphore_signal(sema);
});
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
return number; //doing this will no longer generate an error
}
Then encapsulate your call with another dispatch_async so that your semaphore wait call doesn't block the main thread.
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
[self queryForKey:#"AKey" view:myView countView:myCountView counter:aCounter];
});
I want to call a function in applicationDidEnterBackground this function is defined in other controller.
I have made an object to access it but it seems that function is getting killed when called.
Here is the function it basically calculates the distance and postes a notification
-(void)calculateDistance
{
for (NSMutableDictionary *obj in placeName) {
CLLocation *userLocation = [[AppHelper appDelegate] mLatestLocation];
CLLocation *annotation1 = [[CLLocation alloc] initWithLatitude:[[obj objectForKey:#"Lat"]doubleValue] longitude:[[obj objectForKey:#"long"]doubleValue]];
CGFloat distanceTemp = [annotation1 getDistanceFrom:userLocation];
[obj setObject:[NSNumber numberWithFloat:distanceTemp] forKey:#"distance"];
[annotation1 release];
}
if ([placeName count])
{
NSArray *sortedArray=[placeName sortedArrayUsingFunction:intSort context:NULL];
self.placeName = [NSMutableArray arrayWithArray:sortedArray];
NSMutableArray *arrayTemp = [[NSMutableArray alloc] initWithArray:placeName];
for (int i =0; i < [placeName count]; i++)
{
// NSArray *sortedArray=[placeName sortedArrayUsingFunction:intSort context:NULL];
NSMutableArray *tempArray = [sortedArray objectAtIndex:i];
//DLog(#"sortedArray%#", sortedArray);8=
NSNumber *DistanceNum = [tempArray objectForKey:#"distance"];
NSLog(#"distance%#:::",DistanceNum);
NSInteger intDistance = (int)[DistanceNum floatValue];
if(intDistance<500)
{
NSLog(#"ho gaya bhai");
NSString *notifications =#"Yes";
[[AppHelper mDataManager] setObject:notifications forKey:#"notifications"];
NSLog(#"notifications:%#",notifications);
RemindMeViewController *object = [[RemindMeViewController alloc] initWithNibName:#"RemindMeViewController" bundle:nil];
// RemindMeViewController *object=[[RemindMeViewController alloc]initWithNibName];
NSLog(#"notifications set");
[object scheduleNotification];
}
else
{
// [arrayTemp removeObjectAtIndex:i];
}
}
//after for loop is ended
self.placeName= arrayTemp;
DLog(#"remaining",arrayTemp);
[arrayTemp release];
[mTableView reloadData];
}
}
How long is your function taking to complete? You only have 5 seconds to perform tasks in applicationDidEnterBackground: and return.
From Apple's UIApplicationDelegate Protocol Reference:
Your implementation of this method has approximately five seconds to
perform any tasks and return. If you need additional time to perform
any final tasks, you can request additional execution time from the
system by calling beginBackgroundTaskWithExpirationHandler:. In
practice, you should return from applicationDidEnterBackground: as
quickly as possible. If the method does not return before time runs
out your application is terminated and purged from memory.
You should perform any tasks relating to adjusting your user interface
before this method exits but other tasks (such as saving state) should
be moved to a concurrent dispatch queue or secondary thread as needed.
Because it's likely any background tasks you start in
applicationDidEnterBackground: will not run until after that method
exits, you should request additional background execution time before
starting those tasks. In other words, first call
beginBackgroundTaskWithExpirationHandler: and then run the task on a
dispatch queue or secondary thread.
As far as I know you should not call any time consuming functions in applicationDidEnterBackground since the app will get suspended after a short amount of time.
From Apple's IOS Programming Guide
Most applications that enter the background state are moved to the suspended state shortly thereafter. While in this state, the application does not execute any code and may be removed from memory at any time. Applications that provide specific services to the user can request background execution time in order to provide those services.
Gool luck :)
have you tried this, for example using a NSThread or make some logic to call this method
- (void)applicationDidEnterBackground:(UIApplication *)application {
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, called instead of applicationWillTerminate: when the user quits. */}
// inside this method try to call the calculate position method may be it will works(try nsthread here)
I'm having a hard time figuring out how to put this all together.
I have a puzzle solving app on the mac.
You enter the puzzle, press a button, and while it's trying to find the number of solutions,
min moves and such I would like to keep the UI updated.
Then once it's finished calculating, re-enable the button and change the title.
Below is some sample code from the button selector, and the solving function:
( Please keep in mind I copy/paste from Xcode so there might be some missing {} or
some other typos.. but it should give you an idea what I'm trying to do.
Basicly, user presses a button, that button is ENABLED=NO, Function called to calculate puzzle. While it's calculating, keep the UI Labels updated with moves/solution data.
Then once it's finished calculating the puzzle, Button is ENABLED=YES;
Called when button is pressed:
- (void) solvePuzzle:(id)sender{
solveButton.enabled = NO;
solveButton.title = #"Working . . . .";
// I've tried using this as a Background thread, but I can't get the code to waitTilDone before continuing and changing the button state.
[self performSelectorInBackground:#selector(createTreeFromNode:) withObject:rootNode];
// I've tried to use GCD but similar issue and can't get UI updated.
//dispatch_queue_t queue = dispatch_queue_create("com.gamesbychris.createTree", 0);
//dispatch_sync(queue, ^{[self createTreeFromNode:rootNode];});
}
// Need to wait here until createTreeFromNode is finished.
solveButton.enabled=YES;
if (numSolutions == 0) {
solveButton.title = #"Not Solvable";
} else {
solveButton.title = #"Solve Puzzle";
}
}
Needs to run in background so UI can be updated:
-(void)createTreeFromNode:(TreeNode *)node
{
// Tried using GCD
dispatch_queue_t main_queue = dispatch_get_main_queue();
...Create Tree Node and find Children Code...
if (!solutionFound){
// Solution not found yet so check other children by recursion.
[self createTreeFromNode:newChild];
} else {
// Solution found.
numSolutions ++;
if (maxMoves < newChild.numberOfMoves) {
maxMoves = newChild.numberOfMoves;
}
if (minMoves < 1 || minMoves > newChild.numberOfMoves) {
solutionNode = newChild;
minMoves = newChild.numberOfMoves;
// Update UI on main Thread
dispatch_async(main_queue, ^{
minMovesLabel.stringValue = [NSString stringWithFormat:#"%d",minMoves];
numSolutionsLabel.stringValue = [NSString stringWithFormat:#"%d",numSolutions];
maxMovesLabel.stringValue = [NSString stringWithFormat:#"%d",maxMoves];
});
}
GCD and performSelectorInBackground samples below. But first, let's look at your code.
You cannot wait where you want to in the code above.
Here's the code you had. Where you say wait in the comment is incorrect. See where I added NO.
- (void) solvePuzzle:(id)sender{
solveButton.enabled = NO;
solveButton.title = #"Working . . . .";
// I've tried using this as a Background thread, but I can't get the code to waitTilDone before continuing and changing the button state.
[self performSelectorInBackground:#selector(createTreeFromNode:) withObject:rootNode];
// NO - do not wait or enable here.
// Need to wait here until createTreeFromNode is finished.
solveButton.enabled=YES;
}
A UI message loop is running on the main thread which keeps the UI running. solvePuzzle is getting called on the main thread so you can't wait - it will block the UI. It also can't set the button back to enabled - the work hasn't been done yet.
It is the worker function's job on the background thread to do the work and then when it's done to then update the UI. But you cannot update the UI from a background thread. If you're not using blocks and using performSelectInBackground, then when you're done, call performSelectorOnMainThread which calls a selector to update your UI.
performSelectorInBackground Sample:
In this snippet, I have a button which invokes the long running work, a status label, and I added a slider to show I can move the slider while the bg work is done.
// on click of button
- (IBAction)doWork:(id)sender
{
[[self feedbackLabel] setText:#"Working ..."];
[[self doWorkButton] setEnabled:NO];
[self performSelectorInBackground:#selector(performLongRunningWork:) withObject:nil];
}
- (void)performLongRunningWork:(id)obj
{
// simulate 5 seconds of work
// I added a slider to the form - I can slide it back and forth during the 5 sec.
sleep(5);
[self performSelectorOnMainThread:#selector(workDone:) withObject:nil waitUntilDone:YES];
}
- (void)workDone:(id)obj
{
[[self feedbackLabel] setText:#"Done ..."];
[[self doWorkButton] setEnabled:YES];
}
GCD Sample:
// on click of button
- (IBAction)doWork:(id)sender
{
[[self feedbackLabel] setText:#"Working ..."];
[[self doWorkButton] setEnabled:NO];
// async queue for bg work
// main queue for updating ui on main thread
dispatch_queue_t queue = dispatch_queue_create("com.sample", 0);
dispatch_queue_t main = dispatch_get_main_queue();
// do the long running work in bg async queue
// within that, call to update UI on main thread.
dispatch_async(queue,
^{
[self performLongRunningWork];
dispatch_async(main, ^{ [self workDone]; });
});
}
- (void)performLongRunningWork
{
// simulate 5 seconds of work
// I added a slider to the form - I can slide it back and forth during the 5 sec.
sleep(5);
}
- (void)workDone
{
[[self feedbackLabel] setText:#"Done ..."];
[[self doWorkButton] setEnabled:YES];
}
dispatch_queue_t backgroundQueue;
backgroundQueue = dispatch_queue_create("com.images.bgqueue", NULL);
- (void)process {
dispatch_async(backgroundQueue, ^(void){
//background task
[self processHtml];
dispatch_async(main, ^{
// UI updates in main queue
[self workDone];
});
});
});
}
By and large, any work to be submitted to a background queue needs to follow this pattern of code:
dispatch_queue_t queue = dispatch_queue_create("com.myappname", 0);
__weak MyClass *weakSelf = self; //must be weak to avoid retain cycle
//Assign async work
dispatch_async(queue,
^{
[weakSelf doWork];
dispatch_async(dispatch_get_main_queue(),
^{
[weakSelf workDone];
});
});
queue = nil; //Using ARC, we nil out. Block always retains the queue.
Never Forget:
1 - queue variable above is a reference counted object, because it is a private queue, not a global one. So it is retained by the block which is executing inside that queue. Until this task is complete, it is not released.
2 - Every queue got its own stack which will be allocated / deallocated as part of recursive operation. You only need to worry about class member variables which are reference counted (strong, retain etc.) which are accessed as part of doWork above.
3 - While accessing those reference counted vars inside background queue operation, you need to make them thread-safe, depending on use cases in your app. Examples include writes to objects such as strings, arrays etc. Those writes should be encapsulated inside #synchronized keyword to ensure thread-safe access.
#synchronized ensures no another thread can get access to the resource it protects, during the time the block it encapsulates gets executed.
#synchronized(myMutableArray)
{
//operation
}
In the above code block, no alterations are allowed to myMutableArray inside the #synchronized block by any other thread.
Suppose I run this code:
__block int step = 0;
__block dispatch_block_t myBlock;
myBlock = ^{
if(step == STEPS_COUNT)
{
return;
}
step++;
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
};
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
The block is invoked once from outside. When the inner invocation is reached, the program crashes without any details. If I use direct invocations everywhere instead of GCD dispatches, everything works fine.
I've also tried calling dispatch_after with a copy of the block. I don't know if this was a step in the right direction or not, but it wasn't enough to make it work.
Ideas?
When trying to solve this problem, I found a snippet of code that solves much of the recursive block related issues. I have not been able to find the source again, but still have the code:
// in some imported file
dispatch_block_t RecursiveBlock(void (^block)(dispatch_block_t recurse)) {
return ^{ block(RecursiveBlock(block)); };
}
// in your method
dispatch_block_t completeTaskWhenSempahoreOpen = RecursiveBlock(^(dispatch_block_t recurse) {
if ([self isSemaphoreOpen]) {
[self completeTask];
} else {
double delayInSeconds = 0.3;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), recurse);
}
});
completeTaskWhenSempahoreOpen();
RecursiveBlock allows for non-argument blocks. It can be rewritten for single or multiple argument blocks. The memory management is simplified using this construct, there is no chance of a retain cycle for example.
My solution was derived entirely from Berik's, so he gets all the credit here. I just felt that a more general framework was needed for the "recursive blocks" problem space (that I haven't found elsewhere), including for the asynchronous case, which is covered here.
Using these three first definitions makes the fourth and fifth methods - which are simply examples - possible, which is an incredibly easy, foolproof, and (I believe) memory-safe way to recurse any block to arbitrary limits.
dispatch_block_t RecursiveBlock(void (^block)(dispatch_block_t recurse)) {
return ^() {
block(RecursiveBlock(block));
};
}
void recurse(void(^recursable)(BOOL *stop))
{
// in your method
__block BOOL stop = NO;
RecursiveBlock(^(dispatch_block_t recurse) {
if ( !stop ) {
//Work
recursable(&stop);
//Repeat
recurse();
}
})();
}
void recurseAfter(void(^recursable)(BOOL *stop, double *delay))
{
// in your method
__block BOOL stop = NO;
__block double delay = 0;
RecursiveBlock(^(dispatch_block_t recurse) {
if ( !stop ) {
//Work
recursable(&stop, &delay);
//Repeat
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), recurse);
}
})();
}
You'll note that in the following two examples that the machinery of interacting with the recursion mechanism is extremely lightweight, basically amounting to having to wrap a block in recurse and that block must take a BOOL *stop variable, which should be set at some point to exit recursion (a familiar pattern in some of the Cocoa block iterators).
- (void)recurseTo:(int)max
{
__block int i = 0;
void (^recursable)(BOOL *) = ^(BOOL *stop) {
//Do
NSLog(#"testing: %d", i);
//Criteria
i++;
if ( i >= max ) {
*stop = YES;
}
};
recurse(recursable);
}
+ (void)makeSizeGoldenRatio:(UIView *)view
{
__block CGFloat fibonacci_1_h = 1.f;
__block CGFloat fibonacci_2_w = 1.f;
recurse(^(BOOL *stop) {
//Criteria
if ( fibonacci_2_w > view.superview.bounds.size.width || fibonacci_1_h > view.superview.bounds.size.height ) {
//Calculate
CGFloat goldenRatio = fibonacci_2_w/fibonacci_1_h;
//Frame
CGRect newFrame = view.frame;
newFrame.size.width = fibonacci_1_h;
newFrame.size.height = goldenRatio*newFrame.size.width;
view.frame = newFrame;
//Done
*stop = YES;
NSLog(#"Golden Ratio %f -> %# for view", goldenRatio, NSStringFromCGRect(view.frame));
} else {
//Iterate
CGFloat old_fibonnaci_2 = fibonacci_2_w;
fibonacci_2_w = fibonacci_2_w + fibonacci_1_h;
fibonacci_1_h = old_fibonnaci_2;
NSLog(#"Fibonnaci: %f %f", fibonacci_1_h, fibonacci_2_w);
}
});
}
recurseAfter works much the same, though I won't offer a contrived example here. I am using all three of these without issue, replacing my old -performBlock:afterDelay: pattern.
It looks like there are no problem except delay variable. The block uses always the same time that is generated at line 1. You have to call dispatch_time every time if you want to delay dispatching the block.
step++;
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, dispatch_get_current_queue(), myBlock);
};
EDIT:
I understand.
The block is stored in stack by the block literal. myBlock variable is substituted for the address of the block in stack.
First dispatch_after copied the block from myBlock variable that is the address in stack. And this address is valid at this time. The block is in the current scope.
After that, the block is scoped out. myBlock variable has invalid address at this time. dispatch_after has the copied block in heap. It is safe.
And then, second dispatch_after in the block tries to copy from myBlock variable that is invalid address because the block in stack was already scoped out. It will execute corrupted block in stack.
Thus, you have to Block_copy the block.
myBlock = Block_copy(^{
...
});
And don't forget Block_release the block when you don't need it any more.
Block_release(myBlock);
Opt for a custom dispatch source.
dispatch_queue_t queue = dispatch_queue_create( NULL, DISPATCH_QUEUE_SERIAL );
__block unsigned long steps = 0;
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue);
dispatch_source_set_event_handler(source, ^{
if( steps == STEPS_COUNT ) {
dispatch_source_cancel(source);
return;
}
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC / 2);
dispatch_after(delay, queue, ^{
steps += dispatch_source_get_data(source);
dispatch_source_merge_data(source, 1);
});
});
dispatch_resume( source );
dispatch_source_merge_data(source, 1);
I think you have to copy the block if you want it to stick around (releasing it when you don't want it to call itself anymore).
I'm trying to do something basic to understand threads with a counter that just increments when a button is pressed, and every time the button is pressed, a new thread incrementing the same counter starts. Then I have a stop button to stop a thread that is running. How can I tell how many threads, or which thread is running? Here is my basic template I am working on. Thanks.
-(int)count {
return count;
}
-(void)setCount:(int) value {
count = value;
}
-(void)updateDisplay {
countLabel = [NSString stringWithFormat:#"%i", count];
count++;
}
-(void)myThread {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(updateDisplay)
withObject:nil
waitUntilDone:NO];
[pool release];
}
-(void)startThread {
[self performSelectorInBackground:#selector(myThread) withObject:nil];
}
-(void)myThreadStop {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[self performSelectorOnMainThread:#selector(updateDisplay)
withObject:nil
waitUntilDone:NO];
[pool release];
}
-(void)stopThread {
[self performSelectorInBackground#selector(myThreadStop) withObject:nil];
}
Basically, you want to keep track of the number of threads you have running, and also assign each thread a unique ID. Assuming that startThread is an event handler for your button, you might have something like:
static int threadIndex = 0;
static int threadsRunning = 0;
-(void)startThread {
NSNumber* threadId = [NSNumber numberWithInt:threadIndex++];
threadsRunning++;
[self performSelectorInBackground:#selector(myThread) withObject:threadId];
}
Then when you stop a thread, you just decrement threadsRunning.
Looking at your code, though, I'm confused by your stopTread method, since it seems to be doing the exact same thing as the myThread method, i.e., not stopping the thread at all.
You're performing stuff in the background, which is distinct from explicitly creating threads (e.g. it might reuse threads in a thread pool).
If you want really inefficient threading code, you could use something like this:
NSThread * thread = [[[NSThread alloc] initWithTarget:self selector:#selector(myThread) object:nil] autorelease];
[thread start];
while ([thread isExecuting])
{
NSLog(#"Still running");
[NSThread sleepForTimeInterval:0.1];
}
EDIT: If you're actually going to do iPhone development, I recommend looking at NSOperation/NSInvocationOperation/NSBlockOperation instead. Thread management is a real pain to get right.