9

Reference :

https://stackoverflow.com/a/14741253/1749293

Like the link above said , but it seems that it doesn't explain the reason.

In my code, the following will work :

dispatch_async(dispatch_get_main_queue(), ^{
       [self performSelector:  @selector(helloWorld) withObject:nil afterDelay:0.5];
}); 

but, when i comment something like this , (and I really sure that I run it in the main thread!!)the code doesn't work :

//    dispatch_async(dispatch_get_main_queue(), ^{
        [self performSelector:  @selector(helloWorld) withObject:nil afterDelay: 0.5];
//    });

Can somebody tell me why ? AND ' self ', will nerver release/deallocated , i retain it until the application is over.

" Not Working " , means that , (no crash) it doesn't jump into "helloWorld" method :

-(void) helloWorld {
    NSLog(@"hello world");     // I set a break point here for debug , it wouldn't pause forever
}

I think is the Run Loop cause this problem . Like this link said , but i need more details or more explicit explain.

Community
  • 1
  • 1
isaacselement
  • 2,579
  • 3
  • 22
  • 23
  • 1
    That is odd if you are indeed on the main thread. Any particular reason why you’re not using `dispatch_after()`? –  Jun 06 '13 at 07:41
  • what is BROKER.actors.event? – manujmv Jun 06 '13 at 07:45
  • In your first code you are using `self` while in the seconde you are using `BROKER.actors.event`. So are you sure they are referring to the same thing? – sunkehappy Jun 06 '13 at 07:46
  • 1
    "doesn't work"... meaning? does it crash? in case, what's the log message? – meronix Jun 06 '13 at 07:48
  • and could you please show the starting code of "helloWorld" method? – meronix Jun 06 '13 at 07:52
  • I sorry for the `BROKER.actors.event`, i paste the wrong code. forget it . – isaacselement Jun 06 '13 at 07:57
  • What is the value of aTimeUnit when you call it without dispatch_async ? – Max Jun 06 '13 at 08:05
  • Does it work if you place `[self helloWorld];` right before that `[self performSelector:…` statement? –  Jun 06 '13 at 08:05
  • @Bavarious yes , it does work . – isaacselement Jun 06 '13 at 09:20
  • 2
    Seeing your last edit, it looks like you are **not** on the main thread, in which case @WaltSellers’s answer applies. How exactly did you test that you are on the main thread? Can you write a small test case that reproduces your problem? –  Jun 06 '13 at 09:37
  • You do have a runloop? do you call it from within the app delegate? what is self? – Daij-Djan Jun 16 '13 at 09:55
  • and what is `NSLog(@"%d", [NSThread isMainThread]);` logging – Daij-Djan Jun 16 '13 at 09:56
  • Add this above the `performSelector` call: `NSLog(@"%@ Main thread", [NSThread isMainThread] ? @"ON" : @"NOT ON");` to see if you are running on the main thread or not. – Abizern Jun 16 '13 at 10:00

3 Answers3

25

When I had this kind of thing happen, I was calling performSelector from a GCD dispatch. So it was setting the timer in the GCD worker thread which went away before the timer fired. When GCD removed the worker thread, the timer was lost, so the selector was never called.

Walt Sellers
  • 3,806
  • 30
  • 35
1

EDIT As noted in the comments, performSelector: withObject: afterDelay: also retains your object, so ignore my answer. END EDIT

I asume you are using ARC. Your block is retaining your object.

dispatch_async(dispatch_get_main_queue(), ^{
       [self performSelector:  @selector(helloWorld) withObject:nil afterDelay:aTimeUnit];
});

This is why the selector is fired. When you comment the block, no one retains a reference to your object, so it gets automatically released.

//    dispatch_async(dispatch_get_main_queue(), ^{
        [self performSelector:  @selector(helloWorld) withObject:nil afterDelay: aTimeUnit];
//    });

By the time aTimeUnithas passed, self probably has been released, so the selector call is lost. That's your problem.

You should avoid capturing self inside a block, because if you store the block in an ivar you may end up with a retain cycle, which causes the object not to be deallocated. Here they talk about that: How do I avoid capturing self in blocks when implementing an API?

Community
  • 1
  • 1
The dude
  • 7,896
  • 1
  • 25
  • 50
  • A selector call to a freed object typically ends in a crash, right? – Walt Sellers Jun 06 '13 at 08:07
  • 7
    And I'm pretty sure that performSelector also retains everything as well. – Walt Sellers Jun 06 '13 at 08:09
  • i thought too, as w.sellers, that performSelector become owner of the interested object, retaining it till it execute the selector, so there's no really need to worry for self being deallocated in the meanwhile – meronix Jun 06 '13 at 08:23
0

The Apple Documents said:

This method sets up a timer to perform the aSelector message on the current thread’s run loop.

The timer is configured to run in the default mode (NSDefaultRunLoopMode).

It succeeds if the run loop is running and in the default mode; otherwise, the timer waits until the run loop is in the default mode

Please notice that: It SUCCEEDS only in the default mode(NSDefaultRunLoopMode)

Now. Assume that you have code like this:

dispatch_async(dispatch_queue_create("com.serial.thread", DISPATCH_QUEUE_SERIAL), ^{
    NSLog(@"1");
    [self performSelector:@selector(testLog2) withObject:nil afterDelay:0];
});

PO the [NSRunLoop currentRunLoop] in this Thread:

<CFRunLoop 0x6040001ea000 [0x10c642960]>
{
 wakeup port   = 0x5207, 
 stopped       = false, 
 ignoreWakeUps = true, 
 current mode  = (none),.....
}

As you can see current mode is (none). So the function will never be called!

But if the performSelector:withObject:afterDelay: is in Main Thread,for example, in viewDidLoad, the function will be called.

The following log is [NSRunLoop currentRunLoop] in viewDidLoad:

<CFRunLoop 0x6000001e9700 [0x10c642960]>
{
 wakeup port   = 0x1d03, 
 stopped       = false, 
 ignoreWakeUps = false, 
 current mode  = kCFRunLoopDefaultMode,......
}

@isaacselement

guozqzzu
  • 839
  • 10
  • 12