等到对象可用

时间:2013-11-06 07:53:49

标签: ios objective-c thread-safety

我相信我的用例相当普遍,但我找不到任何可以让我100%确定我在做什么的文档。任何指针都很受欢迎。

在我的应用程序的某个时刻,我开始下载一个对象。之后,用户可以单击按钮。如果对象完成下载,我想执行一些代码。否则我想等到下载对象并执行相同的代码。 如果用户没有点击按钮,我不想做任何事情。下载的对象将丢失。

我的基本想法是做这样的事情:

NSObject *myObj = nil;

- (void)download {
  [self downloadObj:^(NSObject *obj){
    myObj = obj;
  }];
}

- (void)buttonClicked {
  waitOrExecuteDirectly:^{
    // Some code with myObj
  }
}

当然,第一个问题是“我该如何等待?”

所以我尝试了

- (void)buttonClicked {
  if(myObj) {
    // Some code
  } else {
    // Wait then do the exact same code
  }
}

但我认为棘手的问题是“如果对象在”if“计算之后并且在输入”else“块之前完成下载会发生什么?”。

我尝试将下载封装在NSOperation中并使用completionBlock属性。但是如果在设置回调时操作已经完成,则永远不会调用completionBlock。我不想在“下载”方法中设置回调,因为用户可能没有点击按钮。

是否有一个内置机制允许我根据任务状态对要等待或直接执行的任务提供完成回调?如果不是,那么最佳做法是什么?自己做吗?设置和阅读NSLock时使用myObj

3 个答案:

答案 0 :(得分:4)

以下是代码示例:

    - (void)download {
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);
        [self downloadObj:^(NSObject *obj){
            myObj = obj;
            dispatch_semaphore_signal(sema);
        }];
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
        dispatch_release(sema);
    }

答案 1 :(得分:0)

您可以使用dispatch_semaphore_t来完成此任务。创建一个计数为0的信号量,在buttonClicked中等待它,在设置myObj后发出信号。

如果单击按钮时尚未发出信号,则会阻止(注意:您可能不希望在此处任意时间阻塞主线程...),直到信号量为信号。如果首先发信号通知信号量,则等待将简单地将其恢复到其原始状态而不会阻塞。

令人讨厌的是,没有相当于dispatch_group_notify的信号量,所以如果你想在后台等待下载完成,你需要阻止线程等待。

答案 2 :(得分:0)

我认为你不应该等待主线程,因为这会阻塞整个用户界面。我的方法是这样的:

typedef MyOperation void (^)(NSData * myData);

...

@private MyOperation _operationWhenDownloadFinished; // instance variable
@private NSData * _data

...

-(void)buttonPressed{
   [self waitAndPerformOperation:^(NSData * myData){ ... }];
}

-(void)waitAndPerformOperation:(MyOperation)operation{
   if(_data != nil){
       operation(myData);
      _operationWhenDownloadFinished = nil;
   }else{
      _operationWhenDownloadFinished = operation;
   }
}

...

-(void)downloadFinished:(NSData*)downloadedData{ // this is called on the main thread, e.g. by an NSURLConnection delegate
   _data = downloadedData;
   if(_operationWhenDownloadFinished != nil){
      [self waitAndPerformOperation:_operationWhenDownloadFinished];
   }
}

由于按下按钮和downloadFinished委托回调都发生在主线程上,我们可以毫不费力地避免任何竞争条件。