如何在一个特定线程(不是主线程)中使用 运行 方法
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
方法并将其替换为以下内容:
- 获取当前的 NSRunLoop 并将其存储在局部变量中。
- 创建一个间隔为 10 秒的 NSTimer,其目标为
self
,选择器为 heartBeats
。 (稍微更清晰的版本:有另一个方法接受 NSTimer * 但忽略它,所以你的计时器调用那个方法,那个方法调用 heartBeats
。proper 方法来设置一个timer 是给它一个期望用计时器调用的方法,但实际上,给它一个不带参数的方法也有效。)
- 如果您没有使用
scheduledTimerWith…
创建计时器,请将其添加到 运行 循环中。 (scheduledTimerWith…
方法为您做这件事。)
- 创建一个 运行 循环源并将其添加到 运行 循环中。
- 呼叫
[myRunLoop run]
.
第四步熊解释:
Core Foundation(但不是 Foundation;我不知道为什么)有一个叫做“运行 循环源”的东西,它是可以添加到 运行 循环中的自定义对象,并且将一旦发出信号就打电话。
听起来像你想要的,调用你的视图控制器方法!
首先,在视图控制器中,将myThread
从viewDidLoad
中的局部变量更改为实例变量。将其重命名为 _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]
。
所以:
- 视图控制器调用
requestDelegateMessage
requestDelegateMessage
向 运行 循环源发出信号(并唤醒 运行 循环,如果需要的话)
- 运行 循环源在 MyThread 的线程上调用执行回调
- 在 MyThread 的线程上执行回调调用
fireDelegateMessage
fireDelegateMessage
在 MyThread 的线程上调用视图控制器的委托方法实现
- 视图控制器在 MyThread 的线程上调用
someMethod
我在特定线程(不是主线程)中每 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
方法并将其替换为以下内容:
- 获取当前的 NSRunLoop 并将其存储在局部变量中。
- 创建一个间隔为 10 秒的 NSTimer,其目标为
self
,选择器为heartBeats
。 (稍微更清晰的版本:有另一个方法接受 NSTimer * 但忽略它,所以你的计时器调用那个方法,那个方法调用heartBeats
。proper 方法来设置一个timer 是给它一个期望用计时器调用的方法,但实际上,给它一个不带参数的方法也有效。) - 如果您没有使用
scheduledTimerWith…
创建计时器,请将其添加到 运行 循环中。 (scheduledTimerWith…
方法为您做这件事。) - 创建一个 运行 循环源并将其添加到 运行 循环中。
- 呼叫
[myRunLoop run]
.
第四步熊解释:
Core Foundation(但不是 Foundation;我不知道为什么)有一个叫做“运行 循环源”的东西,它是可以添加到 运行 循环中的自定义对象,并且将一旦发出信号就打电话。
听起来像你想要的,调用你的视图控制器方法!
首先,在视图控制器中,将myThread
从viewDidLoad
中的局部变量更改为实例变量。将其重命名为 _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]
。
所以:
- 视图控制器调用
requestDelegateMessage
requestDelegateMessage
向 运行 循环源发出信号(并唤醒 运行 循环,如果需要的话)- 运行 循环源在 MyThread 的线程上调用执行回调
- 在 MyThread 的线程上执行回调调用
fireDelegateMessage
fireDelegateMessage
在 MyThread 的线程上调用视图控制器的委托方法实现- 视图控制器在 MyThread 的线程上调用
someMethod