Padin215
Padin215

Reputation: 7484

performSelector:withObject:afterDelay: not making call

in a method, i want to call a method after n seconds:

    self.toolBarState = [NSNumber numberWithInt:1];
    [self changeButtonNames];
    [self drawMap];
    [self performSelector:@selector(showActionSheet) withObject:nil afterDelay:2];

i want to show the action sheet 2 seconds after drawMap is complete. when i use this performSelector, it never makes the call.

if i just put [self showActionSheet];, it works just fine. is there reason why the performSelector is not making the call?

EDIT: in another part of my code, i make the same call and it works:

HUD = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:HUD];
HUD.delegate = (id) self;
[HUD showWhileExecuting:@selector(drawMap) onTarget:self withObject:nil animated:YES];

[self performSelector:@selector(showActionSheet) withObject:nil afterDelay:6];

here, the showActionSheet gets called 6 seconds after drawMap has completed. i'm guessing there is something going on with the threads that i don't understand yet...

EDIT2:

-(void)showActionSheet
{
    InspectAppDelegate *dataCenter = (InspectAppDelegate *) [[UIApplication sharedApplication] delegate];

    if (dataCenter.fieldIDToPass == nil)
    {
        UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Selected Boundary Options" delegate:(id) self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Analyze a Field",@"Retrieve Saved Analysi", @"Geotag Photos", @"Refresh the map",nil];
        actionSheet.tag = 0;
        [actionSheet showInView:self.view];
    }
    else
    {
        UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Selected Boundary Options" delegate:(id) self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Analyze a Field",@"Retrieve Saved Analysi", @"Geotag Photos", @"Attribute the Field", @"Refresh the map",nil];
        actionSheet.tag = 0;
        [actionSheet showInView:self.view];

    }
}

EDIT3:

ok, so the progress of method calls is:

-(void) foundDoubleTap:(UITapGestureRecognizer *) recognizer
{        
    [HUD showWhileExecuting:@selector(select) onTarget:self withObject:nil animated:YES];
}

-(void) select
{   
        [self changeButtonNames];
        [self drawMap];
        [self performSelector:@selector(showActionSheet) withObject:nil afterDelay:2];
}

showActionSheet never gets called. like i said, i'm pretty sure its a threading issue. if call it with [self showActionSheet], it will run. =/

Upvotes: 17

Views: 13653

Answers (4)

rishi
rishi

Reputation: 11839

Try using:

-(void) select {   
    [self changeButtonNames];
    [self drawMap];
    [self performSelectorOnMainThread:@selector(showActionSheet) withObject:nil waitUntilDone:YES];
}

-performSelector:withObject:afterDelay: schedules a timer on the same thread to call the selector after the passed delay.

Maybe this will work for you:

-(void) select {   
    [self changeButtonNames];
    [self drawMap];
    [self performSelectorOnMainThread:@selector(someA) withObject:nil waitUntilDone:YES];
}

-(void)someA {
    [self performSelector:@selector(showActionSheet) withObject:nil afterDelay:2];
}

Upvotes: 22

Hai Feng Kao
Hai Feng Kao

Reputation: 5288

I got the same problem when I call performSelector:withObject:afterDelay: in a background thread created by ReactiveCocoa. If I execute the block in the ReactiveCocoa's way, the block will be called correctly:

 [[[RACSignal empty] delay:2.0] subscribeCompleted:^(){
    // do something
 }];

I guess there is some magic in the ReactiveCocoa's threading model.

Upvotes: 0

hatunike
hatunike

Reputation: 1977

I ran into this same issue, and by necessity I solve it slightly different from the accepted answer. Notice I wanted my delay and selectors to be variables? Using a block allows me to stay within my current method.

dispatch_async(dispatch_get_main_queue(), ^{
       [self performSelector:loopSelector withObject:nil afterDelay:cycleTime];
});  

By the way, this is definitely a threading issue. The documentation for performSelector:withObject:afterDelay: states that this will be performed on the current thread after the delay, but sometimes that thread's run loop is no longer active.

A more detailed discussion on the subject can be found here

Upvotes: 37

DBD
DBD

Reputation: 23233

Does your class still exist in memory?

If your class goes away before the performSelector fires, do you wind up sending the message to nil (which would cause nothing to occur). You could test this by dropping an NSLog into your dealloc()

You mentioned threads. If your performSelector isn't called from the MainThread it could cause issues (UI things should be done on the main thread).

Upvotes: 1

Related Questions