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

时间:2015-09-24 03:04:39

标签: ios objective-c multithreading cocoa nsthread

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

我像这样<{p>}子类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

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

1 个答案:

答案 0 :(得分:4)

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

你真正要做的就是抛弃整个循环并用NSRunLoop和NSTimer替换它。

  • NSRunLoop只要有需要做的事情就保持线程处于活动状态,但也会在线程中休眠,直到它需要做某事。
  • NSTimer在重复的时间间隔内执行某些操作,只要它在运行循环中安排。

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

  • 向MyThread对象发送heartBeats消息(您正在执行此操作)
  • 向视图控制器发送someMethod消息(这是您询问的内容)

对于后者,你需要另外一件事:一个运行循环源。

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

  1. 获取当前的NSRunLoop并将其存储在本地变量中。
  2. 创建一个间隔为10秒的NSTimer,其目标为self,选择器为heartBeats。 (稍微更干净的版本:有另一种方法,它接受NSTimer *但忽略它,所以你的计时器调用该方法,该方法调用heartBeats正确的方式设置一个计时器是给它一个期望用计时器调用的方法,但实际上,给它一个不带参数的方法也可以。)
  3. 如果您没有使用scheduledTimerWith…创建计时器,请将其添加到运行循环中。 (scheduledTimerWith…方法为您执行此操作。)
  4. 创建一个运行循环源并将其添加到运行循环中。
  5. 致电[myRunLoop run]
  6. 第4步可以解释:

    核心基金会(但不是基金会;我不知道为什么)有一个叫做“运行循环源”的东西,它是可以添加到运行循环中的自定义对象,一旦发出信号就会调用它。

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

    首先,在视图控制器中,将myThread中的viewDidLoad中的局部变量更改为实例变量。将其重命名_myThread以使其清晰。

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

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

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

    在演出回调中,您需要执行以下操作:

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

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

    接下来,将一个公共方法添加到MyThread(即,在MyThread.h中声明它以及在MyThread.m中实现它),其名称类似于“requestDelegateMessage”。在此方法中,在运行循环源上调用CFRunLoopSourceSignal。 (该功能的文档表明您还需要在主题CFRunLoopWakeUp上致电CFRunLoop。请先试用。)

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

    所以:

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