Objective-C:如果线程不是主要线程,则不会调用[NSObject performSelector:onThread ...] [英] Objective-C: [NSObject performSelector: onThread...] does not get called if the thread is not the main one

查看:108
本文介绍了Objective-C:如果线程不是主要线程,则不会调用[NSObject performSelector:onThread ...]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此处,已经讨论了非常类似的问题.当前的问题以及我要解决的问题是在创建对象的线程中的给定对象上调用函数.这是完整的情况:

Very similar issue is already discussed here. The problem at hand and what I am trying to achieve is to call a function on a given object in the thread it is created at. Here is the complete case:

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

  1. an instance of class A is created in a given NSThread (Thread A) (not the main one). The instance keeps its creating NSThread as a member variable.
  2. an instance of class B has one of its member functions executing in another NSThread - Thread B, and wants to call a function of A in A's creation thread. Thus B's currently executing function issues the following call:

[_a performSelector: @(fun) 
           onThread: _a.creationThread
         withObject: nil
      waitUntilDone: NO];

如果A实例的创建线程不是主要实例,则不会调用fun.如果创建线程是主要线程,则始终会调用它.首先,我在考虑创建A实例的线程是否已被破坏,并且指针指向无效线程,但实际上调用线程对象(线程A)上的任何函数均会产生有效结果,并且不会崩溃.还根据此检查来检查对象是否有效.有什么建议吗?

If the creation thread of A's instance is not the main one, fun never gets called. If the creation thread is the main one it is always called. First I was thinking whether the thread that created A's instance has been destroyed and the pointer points to an invalid thread but actually calling any functions on the thread object (Thread A) produces valid results and no crashes. Also checking the object is valid according to this check. Any suggestions?

更新:

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

What I'm doing is creating a timer on a background thread:

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

此代码是启动计时器的代码.计时器未在任何特定的后台线程中启动.碰巧,可以在任何线程中调用创建计时器的函数.因此,计时器应该在与NSTimer文档所述完全相同的计时器中失效:您应该始终从安装计时器的同一线程中调用invalidate方法."

This code is the one starting the timer. The timer is not started in any specific background thread. It just happens that the function that creates the timer could be called in any thread. Thus the timer should be invalidated in the exactly same one as the NSTimer documentation states: "you should always call the invalidate method from the same thread on which the timer was installed."

推荐答案

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

To run timer on background thread, you have two options.

  1. 使用调度计时器源:

  1. Use dispatch timer source:

@property (nonatomic, strong) dispatch_source_t timer;

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

and you can then configure this timer to fire every two seconds:

- (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;
}

  • 在后台线程上运行NSTimer.为此,您可以执行以下操作:

  • Run NSTimer on background thread. To do this, you can do something like:

    @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

    I'm not crazy about the shouldKeepRunning state variable, but if you look at the Apple documentation for the run method, they discourage the reliance upon adding sources/timers to run loops:

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

    If you want the run loop to terminate, you shouldn't use this method. Instead, use one of the other run methods and also check other arbitrary conditions of your own, in a loop. A simple example would be:

    BOOL shouldKeepRunning = YES;        // global
    NSRunLoop *theRL = [NSRunLoop currentRunLoop];
    while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
    

  • 我个人建议使用调度计时器方法.

    Personally, I'd recommend the dispatch timer approach.

    这篇关于Objective-C:如果线程不是主要线程,则不会调用[NSObject performSelector:onThread ...]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    相关文章
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆