Reputation: 2689
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.
Upvotes: 9
Views: 9874
Reputation: 899
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
Upvotes: 0
Reputation: 7924
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 aTimeUnit
has 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?
Upvotes: 1
Reputation: 3939
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.
Upvotes: 25