Objective-C:如果线程不是主线程,则不会调用[NSObject performSelector:onThread ...]

时间:2016-06-25 16:21:22

标签: objective-c multithreading nsthread performselector

已经讨论了非常类似的问题here。手头的问题和我想要实现的是在创建它的线程中调用给定对象的函数。以下是完整的案例:

  1. 在给定的NSThread(线程A)(不是主要的)中创建A类的实例。该实例将其NSThread创建为成员变量。
  2. B类的一个实例的一个成员函数在另一个NSThread - 线程B中执行,并希望在A的创建线程中调用A的函数。因此B当前正在执行的函数发出以下调用:

    [_a performSelector: @(fun) 
               onThread: _a.creationThread
             withObject: nil
          waitUntilDone: NO];
    
  3. 如果A实例的创建线程不是主要实例,则永远不会调用fun。如果创建线程是主线程,则始终调用它。首先,我在考虑创建A实例的线程是否已被破坏,指针指向无效线程,但实际调用线程对象(线程A)上的任何函数都会生成有效结果而不会崩溃。根据{{​​3}}检查对象也是有效的。有什么建议吗?

    更新:

    我正在做的是在后台线程上创建一个计时器:

    _timer = [NSTimer timerWithTimeInterval:60.0 target:self selector:@selector(fun:) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
    

    此代码是启动计时器的代码。计时器不在任何特定的后台线程中启动。只是在任何线程中都可以调用创建计时器的函数。因此,计时器应该与NSTimer文档中完全相同的无效:“你应该总是从安装了计时器的同一个线程中调用invalidate方法。”

1 个答案:

答案 0 :(得分:2)

要在后台线程上运行计时器,您有两个选择。

  1. 使用调度计时器源:

    @property (nonatomic, strong) dispatch_source_t timer;
    

    然后您可以将此计时器配置为每两秒触发一次:

    - (void)startTimer {
        dispatch_queue_t queue = dispatch_queue_create("com.domain.app.timer", 0);
        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 2.0 * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
        dispatch_source_set_event_handler(self.timer, ^{
            // call whatever you want here
        });
        dispatch_resume(self.timer);
    }
    
    - (void)stopTimer {
        dispatch_cancel(self.timer);
        self.timer = nil;
    }
    
  2. 在后台线程上运行NSTimer。为此,您可以执行以下操作:

    @property (atomic) BOOL shouldKeepRunning;
    @property (nonatomic, strong) NSThread *timerThread;
    @property (nonatomic, strong) NSTimer *timer;
    

    - (void)startTimerThread {
        self.timerThread = [[NSThread alloc] initWithTarget:self selector:@selector(startTimer:) object:nil];
        [self.timerThread start];
    }
    
    - (void)stopTimerThread {
        [self performSelector:@selector(stopTimer:) onThread:self.timerThread withObject:nil waitUntilDone:false];
    }
    
    - (void)startTimer:(id)__unused object {
        @autoreleasepool {
            NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
            self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(handleTimer:) userInfo:nil repeats:YES];
            [runLoop addTimer:self.timer forMode:NSDefaultRunLoopMode];
    
            self.shouldKeepRunning = YES;
            while (self.shouldKeepRunning && [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
                ;
    
            self.timerThread = nil;
        }
    }
    
    - (void)handleTimer:(NSTimer *)timer {
        NSLog(@"tick");
    }
    
    - (void)stopTimer:(id)__unused object {
        [self.timer invalidate];
        self.timer = nil;
        self.shouldKeepRunning = FALSE;
    }
    

    我对shouldKeepRunning状态变量并不感到痴迷,但是如果你看一下Apple documentation for the run method,他们就不会依赖添加源/定时器来运行循环:

      

    如果希望运行循环终止,则不应使用此方法。相反,使用其他run方法之一,并在循环中检查您自己的其他任意条件。一个简单的例子是:

    BOOL shouldKeepRunning = YES;        // global
    NSRunLoop *theRL = [NSRunLoop currentRunLoop];
    while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
    
  3. 就个人而言,我建议使用调度计时器方法。