如何在一个特定线程(不是主线程)中使用 运行 方法

how to run methods in one specific thread (not main thread)

我在特定线程(不是主线程)中每 10ms 调用一个 heartBeats 方法,如何在同一个线程中随时调用另一个方法?

我像这样子类 NSThread

@implementation MyThread
{
    NSTimeInterval _lastTimeInterval;
}

- (void)main
{

    while (true) {

        NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970]*1000;

        if (timeInterval - _lastTimeInterval > 10)
        {
            [self heartBeats];

            _lastTimeInterval = timeInterval;
        }

    }

}

- (void)heartBeats
{
    NSLog(@"heart beats thread: %@", [NSThread currentThread].description);
}

@end

和运行像这样

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    NSLog(@"main thread: %@", [NSThread currentThread].description);

    MyThread *myThread = [[MyThread alloc]init];
    [myThread start];
}


- (void)someMethod
{
    // do somthing
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

@end

现在,问题来了,如何在myThread中运行- (void)someMethod

NSThread 子类的 main 方法是该线程上 运行 的所有内容。如果没有 main 方法的合作,你不能将它中断到 运行 其他代码。

你真正应该做的是扔掉整个循环并用 NSRunLoop 和 NSTimer 替换它。

  • NSRunLoop 保持线程活动,只要它需要做某事,但也会让线程休眠 直到 它需要做某事。
  • NSTimer 会在重复间隔内执行某些操作,只要它被安排在 运行 循环中。

您需要您的线程做两件事:

  • 向 MyThread 对象发送 heartBeats 消息(您正在这样做)
  • 向视图控制器发送 someMethod 消息(这就是你问的)

对于后者,您还需要一件事:运行 循环源。

因此,清除您的 main 方法并将其替换为以下内容:

  1. 获取当前的 NSRunLoop 并将其存储在局部变量中。
  2. 创建一个间隔为 10 秒的 NSTimer,其目标为 self,选择器为 heartBeats。 (稍微更清晰的版本:有另一个方法接受 NSTimer * 但忽略它,所以你的计时器调用那个方法,那个方法调用 heartBeatsproper 方法来设置一个timer 是给它一个期望用计时器调用的方法,但实际上,给它一个不带参数的方法也有效。)
  3. 如果您没有使用 scheduledTimerWith… 创建计时器,请将其添加到 运行 循环中。 (scheduledTimerWith… 方法为您做这件事。)
  4. 创建一个 运行 循环源并将其添加到 运行 循环中。
  5. 呼叫[myRunLoop run].

第四步熊解释:

Core Foundation(但不是 Foundation;我不知道为什么)有一个叫做“运行 循环源”的东西,它是可以添加到 运行 循环中的自定义对象,并且将一旦发出信号就打电话。

听起来像你想要的,调用你的视图控制器方法!

首先,在视图控制器中,将myThreadviewDidLoad中的局部变量更改为实例变量。将其重命名为 _myThread 以明确说明。

接下来,在MyThread中添加一个delegate 属性。这应该是 weak 并且类型是 id <MyThreadDelegate>。您还需要定义一个 MyThreadDelegate 协议,它应该有一个不带参数且不返回任何内容的方法 (void)。

您现在应该能够从视图控制器设置 _myThread.delegate = self,并在视图控制器中实现您在协议中声明的方法。视图控制器现在是其 MyThread 的委托。

-[MyThread main] 中,创建版本 0 CFRunLoopSource。 Create 函数采用“上下文”结构,其中包含版本 (0)、“信息”指针(将其设置为 self,即 MyThread)和执行回调(函数,将使用 info 指针作为其唯一参数调用。

在执行回调中,您需要执行如下操作:

MyThread *self = (__bridge MyThread *)info;
[self fireDelegateMessage];

在 MyThread 中,实现 fireDelegateMessage 方法。在那里,发送 self.delegate 您在协议中声明的消息。

接下来,向 MyThread 添加一个 public 方法(即,在 MyThread.h 中声明它并在 MyThread.m 中实现它),名称类似于“requestDelegateMessage”。在此方法中,在 运行 循环源上调用 CFRunLoopSourceSignal。 (该函数的文档建议您还需要在线程的 CFRunLoop 上调用 CFRunLoopWakeUp。先尝试一下。)

最后,当视图控制器希望在该线程上调用 someMethod 时,调用 [_myThread requestDelegateMessage]

所以:

  1. 视图控制器调用 requestDelegateMessage
  2. requestDelegateMessage 向 运行 循环源发出信号(并唤醒 运行 循环,如果需要的话)
  3. 运行 循环源在 MyThread 的线程上调用执行回调
  4. 在 MyThread 的线程上执行回调调用 fireDelegateMessage
  5. fireDelegateMessage 在 MyThread 的线程上调用视图控制器的委托方法实现
  6. 视图控制器在 MyThread 的线程上调用 someMethod