iPhone 4 is there an absolutely certain way to have a long term NSTimer fire

后端 未结 3 857
予麋鹿
予麋鹿 2021-01-15 05:52

I keep having troubles with my NSTimers and background selectors. It is driving me nuts and takes a very long time to try out each tweak. To preserve my sanity, and the sani

3条回答
  •  梦毁少年i
    2021-01-15 06:01

    For as long as you depend on using scheduledTimerWithTimeInterval:... you cannot achieve what you want:
    The timer will always be tied to the run-loop of the calling thread.

    If there is no run-loop associated with that thread by the time of that message's invocation, there surely is one when the method returns as -[NSRunLoop currentRunLoop] creates a run-loop if necessary.

    What you can do, if you don't like the other APIs for creation of a timer, is providing a category on NSTimer, which takes care of all the scheduling and so forth and that you can reuse in other projects.

    Here is an example of what such a category might look like:

    #pragma mark - setting up a timer:
    + (NSTimer *)yourPrefix_mainLoopScheduledTimerWithTimeInterval:(NSTimeInterval)interval target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
    {
        NSTimer *timer = [self yourPrefix_timerWithTimeInterval:interval target:target selector:selector userInfo:userInfo repeats:shouldRepeat];
    
        void (^scheduler)() = ^{
            [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
        };
    
        if ([NSThread isMainThread]) {
            scheduler();
        } else {
            // you should really be able to rely on the fact, that the timer is ready to roll, when this method returns
            dispatch_sync(dispatch_get_main_queue(), scheduler);
        }
    
        return timer;
    }
    
    // this is just a convenience for the times where you actually want an _unscheduled_ timer
    + (NSTimer *)yourPrefix_timerWithTimeInterval:(NSTimeInterval)interval target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
    {
        NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:interval];
    
        NSTimer *timer = [[self alloc] initWithFireDate:fireDate interval:interval target:target selector:selector userInfo:userInfo repeats:shouldRepeat];
    
        return [timer autorelease];
    }
    
    #pragma mark - tearing it down:
    - (void)yourPrefix_invalidateMainLoopTimer
    {
        [self yourPrefix_invalidateMainLoopTimerAsynchronous:NO];
    }
    
    - (void)yourPrefix_invalidateMainLoopTimerAsynchronous:(BOOL)returnsImmediately
    {
        void (^invalidator)() = ^{
            [self invalidate];
        };
    
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        if (returnsImmediately) {
            dispatch_async(mainQueue, invalidator);
            return;
        }
    
        if (![NSThread isMainThread]) {
            dispatch_sync(mainQueue, invalidator);
            return;
        }
    
        invalidator();
    }
    

    Note the thread checks before using dispatch_sync because...

    dispatch_sync

    Discussion

    […] Calling this function and targeting the current queue results in deadlock.

    (from The GCD Reference — emphasis mine)

提交回复
热议问题