17

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. =/

Padin215
  • 7,444
  • 13
  • 65
  • 103

4 Answers4

37

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

Community
  • 1
  • 1
hatunike
  • 1,967
  • 1
  • 19
  • 26
  • 1
    Yep, I recently updated the MBProgressHUD library and in their how-to, they suggest getting the main thread and running the methods just like this. I've switch to doing it this way as well. – Padin215 Feb 14 '13 at 15:15
  • 1
    The OP needs the main thread to show an action sheet. My problem was a little different, but this was still basically a good solution. I used `dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0 ), ^{` rather than run on the main queue. – jab Mar 19 '14 at 23:15
22

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];
}
Stunner
  • 12,025
  • 12
  • 86
  • 145
rishi
  • 11,779
  • 4
  • 40
  • 59
  • 1
    this does work, the method gets called but there is no delay which is what i'm trying to do. i want showActionSheet to wait n seconds after drawMap is complete (i'm making a change to the view and want the user to see the change before the action sheet is brought up). – Padin215 Jan 05 '12 at 17:18
  • 1
    Yes.there is no delay on main thread methods, you can try with edited part above – rishi Jan 05 '12 at 17:24
  • ha, that works perfectly! think you could explain why this works? i'm guessing it has something to do with the threads, where this is getting process back onto the main thread which does get killed? – Padin215 Jan 05 '12 at 17:34
  • showWhileExecuting works on a new thread, so from there before calling any other selector you need to move to main thread. That is main logic. For more information you can check - http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSThread_Class/Reference/Reference.html – rishi Jan 05 '12 at 17:40
  • 1
    awesome, thanks for the info. i added in some NSLog(@"%@", [NSThread currentThread]) and ya, as soon as it goes to -(void)select its a new thread. with the fix, when it gets to -(void)showActionSheet its back on the main thread. – Padin215 Jan 05 '12 at 17:45
1

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).

DBD
  • 23,075
  • 12
  • 60
  • 84
  • 1
    no, the class does not go away. i'm just confused why [self showActionSheet] works while the performSelector does not. – Padin215 Jan 05 '12 at 17:13
0

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.

Hai Feng Kao
  • 5,219
  • 2
  • 27
  • 38