6

我目前正在开发一个 iPhone 应用程序,并且我有一个来自第三方的库,它具有异步行为,但我想用我自己的类进行包装并使其看起来是同步的。

这个库中的中心类,我们称之为 Connection 类,有几个函数,当调用委托类的实例上的方法时,它们的最终结果会被解析。我想要做的是包装这个类和委托,使它看起来是同步的而不是异步的。如果我在 Java 中执行此操作,我会使用 FutureTask 或 CountdownLatch 或只是 join()。但我不确定在 Objective C 中执行此操作的最佳方法。

我首先创建了一个符合上述委托协议的 NSThread 扩展 NFCThread。这个想法是我将初始化和 NFCThread,将 NFCThread 实例传递给 Connection 的 setDelegate 方法,启动线程,然后在 Connection 上调用异步方法。我的期望是调用 NFCThread 实例上的三个委托方法之一,最终导致线程退出。

为了模拟加入,我做了以下事情。我在 NFCThread 中添加了一个 NSConditionalLock:

joinLock = [[NSConditionLock alloc] initWithCondition:NO];

调用 Connection 的代码如下所示:

NFCThread *t = [[NFCThread alloc] init];
[connection setDelegate:t];
[t start];

[connection openSession];
// Process errors, etc...

[t.joinLock lockWhenCondition:YES];
[t.joinLock unlock];
[t release];
[connection setDelegate:nil];

委托的协议具有三种方法。在 NFCThread 中,我实现了每个方法,如下所示:

- (void)didReceiveMessage:(CommandType)cmdType 
                     data:(NSString *)responseData 
               length:(NSInteger)length {
    NSLog(@"didReceiveMessage");
    // Do something with data and cmdType...
    [joinLock lock];
    [joinLock unlockWithCondition:YES];
    callBackInvoked = YES;
}

我重载了 NFCThread 的 main 方法,使它不断循环。像这样的东西:

while (!callBackInvoked) { ; }

我发现这并不是一个好主意,因为它会导致 CPU 使用率飙升。因此,我尝试使用我在此站点上找到的一些示例中的运行循环:

NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];

while (!callBackInvoked) {
    [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}

在我的两个实现中,主线程总是被阻塞,并且似乎没有调用任何委托方法。但是,我知道该库运行正常,并且通常调用委托方法。

我觉得我在这里遗漏了一些明显的东西。非常感谢任何帮助。

富有的

4

4 回答 4

4

您需要一个信号量,这将允许您的主代码路径阻塞,直到您的异步回调向信号量发出信号并允许它继续。

信号量可通过 Grand Central Dispatch 在 iOS 4 中使用。

看来信号量的行为可以在 iOS 3 中使用 NSCondition 实现。

于 2010-08-05T23:10:32.480 回答
3

我刚刚实现了类似的东西。上下文是: - 在后台线程中包装对 ZipArchive 的多个调用 - 对于每次解压缩调用,显示不同的进度表(无限旋转的轮子显示正在扩展的文件名) - 当所有存档都已完成时执行清理任务展开

原来 NSConditionLock 让它变得简单,代码如下:

NSConditionLock* lock = alloc/init
for(int idx = 0; idx < [list count]; idx++) {
  NSString* fileName = ["getIdx's element in list"]
  [cond lockWhenCondition:idx]; //

  ... prepare things before forking

  [cond unlockWithCondition:-1];
  [Notification forkBlock:^(void){
    [cond lockWhenCondition:-1];

    NSAutoreleasePool *pool = [alloc/init];
    [ZipArchive unzipFile:fileName];
    [pool release];
    [cond unlockWithCondition:idx+1];
  }];
}

[cond lockWhenCondition:[list count];
// all the tasks are now completed

每个块都安排在后台线程中。Notification 类负责使用旋转轮为 UIView 设置动画,并将块包装在另一个线程中。必须从同一个线程调用锁定/解锁,因此条件是启用从线程和后台线程之间的乒乓球(-1 表示后台,1,2,3..n 表示前台)。

于 2011-10-07T23:13:22.590 回答
0

好的,所以这里有一些不同的问题,我正在考虑从哪里开始。

但是,为了让我们了解您要完成的工作,当您说您希望调用“出现”同步时,您的意思是您希望调用阻塞吗?您是从主线程进行此调用吗?如果是这样,那么您似乎在设计上阻塞了主线程。

请记住,第三方库可能正在主运行循环上调度事件。您可以创建自己的运行循环并在另一个线程中运行它,但是您是否告诉其他库使用该运行循环来处理其事件?(例如,它可能会发出在您已阻止的主运行循环上安排的异步网络请求)

我会重新考虑你在做什么,但首先我们需要知道你的意图是否是阻止你进行此调用的线程。另外,你需要支持 iPhoneOS 3.x 吗?

于 2010-08-05T23:44:11.523 回答
0

您要做的是使用 NSCondition 在当前线程上等待,并在第二个线程上发出信号让第一个线程继续。

- (void) firstThread
{
    workIsDone = NO;
    //Start second thread here
    [condition lock];
    while (!workIsDone) {
        [condition wait];
    }
    [condition unlock];

    // Keep going
}

- (void) secondThread
{
    [condition lock];

    //Do some work.
    workIsDone = YES;
    [condition signal];
    [condition unlock];
}
于 2011-12-16T15:28:51.593 回答