Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iPhone: how to use performSelector:onThread:withObject:waitUntilDone: method?

I am trying to use a separate thread for working with some API.

The problem is that I am not able to use performSelector:onThread:withObject:waitUntilDone: method with a thread that I' instantiated for this.

My code:

@interface MyObject : NSObject {
  NSThread *_myThread;
}
@property(nonatomic, retain) NSThread *myThread;
@end

@implementation MyObject
@synthesize myThread = _myThread;
- (NSThread *)myThread {
  if (_myThread == nil) {
    NSThread *myThreadTemp = [[NSThread alloc] init];
    [myThreadTemp start];
    self. myThread = myThreadTemp;
    [myThreadTemp release];
  }
  return _myThread;
}

- (id)init {
  if (self = [super init]) {
    [self performSelector:@selector(privateInit:) onThread:[self myThread] withObject:nil waitUntilDone:NO];
  }
  return self;
}
- (void)privateInit:(id)object {
  NSLog(@"MyObject - privateInit start");
}

- (void)dealloc {
  [_myThread release];
  _myThread = nil;
  [super dealloc];
}
@end

"MyObject - privateInit start" is never printed.
What am I missing?

I tried to instantiate the thread with target and selector, tried to wait for method execution completion (waitUntilDone:YES).
Nothing helps.

UPDATE:
I don't need this multithreading for separating costly operations to another thread.
In this case I could use the performSelectorInBackground as mentioned in few answers.
The main reason for this separate thread is the need to perform all the actions in the API (TTS by Loquendo) from one single thread.
Meaning that I have to create an instance of the TTS object and call methods on that object from the same thread all the time.

like image 529
Michael Kessler Avatar asked Apr 06 '10 11:04

Michael Kessler


3 Answers

I found an answer!

In order to keep the thread up, there is a need in additional piece of code:

- (void)threadMain:(id)data {
    NSAutoreleasePool *pool = [NSAutoreleasePool new];

    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    [runloop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];

    while (isAlive) { // 'isAlive' is a variable that is used to control the thread existence...
        [runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    }

    [pool release];
}


And the next line:

NSThread *myThreadTemp = [[NSThread alloc] init];

Should be replaced by this one:

NSThread *myThreadTemp = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain:) object:nil];

EDIT: As was suggested by few people here I've added few lines of code (NSAutoreleasePool, addPort method and 'isAlive' boolean).

like image 143
Michael Kessler Avatar answered Nov 16 '22 18:11

Michael Kessler


This is what works for me. Main loop taken from Apple's documentation http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW25

- (void) start {
    self.imageSaverThread = [[[NSThread alloc] initWithTarget:self selector:@selector(imageSaverThreadMain) object:nil] autorelease];
    [self.imageSaverThread start];
}

- (void) imageSaverKeepAlive {
    [self performSelector:@selector(imageSaverKeepAlive) withObject:nil afterDelay:60];    
}

- (void)imageSaverThreadMain
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    // Add selector to prevent CFRunLoopRunInMode from returning immediately
    [self performSelector:@selector(imageSaverKeepAlive) withObject:nil afterDelay:60];
    BOOL done = NO;

    do
    {
        NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init];
        // Start the run loop but return after each source is handled.
        SInt32    result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 10, YES);

        // If a source explicitly stopped the run loop, or if there are no
        // sources or timers, go ahead and exit.
        if ((result == kCFRunLoopRunStopped) || (result == kCFRunLoopRunFinished))
            done = YES;

        [tempPool release];
    }
    while (!done);

    [pool release];
}

Hope it helps

like image 13
Przemyslaw Zych Avatar answered Nov 16 '22 17:11

Przemyslaw Zych


Well, I suppose I've got a better solution

- (void)run{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    running = true;
    [[NSRunLoop currentRunLoop] addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
    while (running && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]){
        //run loop spinned ones
    }

    [pool release];
}

What am i doing here?
1) Adding a mock port here as a Source will prevent runMode:beforeDate: method from exiting immideately.
2) Method runMode:beforeDate: blocks thread until there's something in runLoop.

like image 4
Andrey Lushnikov Avatar answered Nov 16 '22 18:11

Andrey Lushnikov