GCD,线程,程序stream和UI更新

我很难弄清楚如何把这一切放在一起。 我在Mac上有一个解谜应用程序。 你input谜题,按下一个button,当它试图find解决scheme的数量,分钟移动等,我想保持UI更新。 然后一旦完成计算,重新启用button并更改标题。

下面是一些示例代码从buttonselect器和解决函数:(请记住我从Xcode复制/粘贴,以便可能有一些失踪或其他一些错别字..但它应该让你知道我是什么,米试图做。

基本上,用户按下一个button,那个button是ENABLED = NO,函数调用来计算拼图。 在计算时,请使用移动/解决scheme数据更新UI标签。 然后一旦完成计算拼图,Button是ENABLED = YES;

按下button时调用:

- (void) solvePuzzle:(id)sender{ solveButton.enabled = NO; solveButton.title = @"Working . . . ."; // I've tried using this as a Background thread, but I can't get the code to waitTilDone before continuing and changing the button state. [self performSelectorInBackground:@selector(createTreeFromNode:) withObject:rootNode]; // I've tried to use GCD but similar issue and can't get UI updated. //dispatch_queue_t queue = dispatch_queue_create("com.gamesbychris.createTree", 0); //dispatch_sync(queue, ^{[self createTreeFromNode:rootNode];}); } // Need to wait here until createTreeFromNode is finished. solveButton.enabled=YES; if (numSolutions == 0) { solveButton.title = @"Not Solvable"; } else { solveButton.title = @"Solve Puzzle"; } } 

需要在后台运行,以便UI可以更新:

 -(void)createTreeFromNode:(TreeNode *)node { // Tried using GCD dispatch_queue_t main_queue = dispatch_get_main_queue(); ...Create Tree Node and find Children Code... if (!solutionFound){ // Solution not found yet so check other children by recursion. [self createTreeFromNode:newChild]; } else { // Solution found. numSolutions ++; if (maxMoves < newChild.numberOfMoves) { maxMoves = newChild.numberOfMoves; } if (minMoves < 1 || minMoves > newChild.numberOfMoves) { solutionNode = newChild; minMoves = newChild.numberOfMoves; // Update UI on main Thread dispatch_async(main_queue, ^{ minMovesLabel.stringValue = [NSString stringWithFormat:@"%d",minMoves]; numSolutionsLabel.stringValue = [NSString stringWithFormat:@"%d",numSolutions]; maxMovesLabel.stringValue = [NSString stringWithFormat:@"%d",maxMoves]; }); } 

GCD和performSelectorInBackground示例如下。 但首先,让我们看看你的代码。

您不能等待上面的代码中您想要的位置。 这是你的代码。 你说什么等在评论是不正确的。 看看我加了NO。

 - (void) solvePuzzle:(id)sender{ solveButton.enabled = NO; solveButton.title = @"Working . . . ."; // I've tried using this as a Background thread, but I can't get the code to waitTilDone before continuing and changing the button state. [self performSelectorInBackground:@selector(createTreeFromNode:) withObject:rootNode]; // NO - do not wait or enable here. // Need to wait here until createTreeFromNode is finished. solveButton.enabled=YES; } 

UI消息循环运行在保持UI运行的主线程上。 solvePuzzle在主线程中被调用,所以你不能等待 – 它会阻止用户界面。 它也不能将button设置为启用 – 工作尚未完成。

这是后台线程上的工作函数的工作,然后完成工作,然后更新UI。 但是你不能从后台线程更新UI。 如果你没有使用块并使用performSelectInBackground,那么当你完成后,调用performSelectorOnMainThread来调用select器来更新你的UI。

performSelectorInBackground示例:

在这个片段中,我有一个调用长时间运行的工作的button,一个状态标签,我添加了一个滑块来显示我可以在bg工作完成时移动滑块。

 // on click of button - (IBAction)doWork:(id)sender { [[self feedbackLabel] setText:@"Working ..."]; [[self doWorkButton] setEnabled:NO]; [self performSelectorInBackground:@selector(performLongRunningWork:) withObject:nil]; } - (void)performLongRunningWork:(id)obj { // simulate 5 seconds of work // I added a slider to the form - I can slide it back and forth during the 5 sec. sleep(5); [self performSelectorOnMainThread:@selector(workDone:) withObject:nil waitUntilDone:YES]; } - (void)workDone:(id)obj { [[self feedbackLabel] setText:@"Done ..."]; [[self doWorkButton] setEnabled:YES]; } 

GCD示例:

 // on click of button - (IBAction)doWork:(id)sender { [[self feedbackLabel] setText:@"Working ..."]; [[self doWorkButton] setEnabled:NO]; // async queue for bg work // main queue for updating ui on main thread dispatch_queue_t queue = dispatch_queue_create("com.sample", 0); dispatch_queue_t main = dispatch_get_main_queue(); // do the long running work in bg async queue // within that, call to update UI on main thread. dispatch_async(queue, ^{ [self performLongRunningWork]; dispatch_async(main, ^{ [self workDone]; }); }); } - (void)performLongRunningWork { // simulate 5 seconds of work // I added a slider to the form - I can slide it back and forth during the 5 sec. sleep(5); } - (void)workDone { [[self feedbackLabel] setText:@"Done ..."]; [[self doWorkButton] setEnabled:YES]; } 
  dispatch_queue_t backgroundQueue; backgroundQueue = dispatch_queue_create("com.images.bgqueue", NULL); - (void)process { dispatch_async(backgroundQueue, ^(void){ //background task [self processHtml]; dispatch_async(main, ^{ // UI updates in main queue [self workDone]; }); }); }); } 

总的来说,任何要提交给后台队列的工作都需要遵循以下代码模式:

 dispatch_queue_t queue = dispatch_queue_create("com.myappname", 0); __weak MyClass *weakSelf = self; //must be weak to avoid retain cycle //Assign async work dispatch_async(queue, ^{ [weakSelf doWork]; dispatch_async(dispatch_get_main_queue(), ^{ [weakSelf workDone]; }); }); queue = nil; //Using ARC, we nil out. Block always retains the queue. 

永远不要忘记:

1 – 上面的队列variables是一个引用计数的对象 ,因为它是一个私有队列,不是全局的。 所以它被在该队列中执行的块所保留。 在这个任务完成之前,它不会被释放。

2 – 每个队列都有自己的堆栈,它将作为recursion操作的一部分被分配/解除分配。 您只需要担心引用计数(强,保留等)的类成员variables,这些variables是作为doWork的一部分访问的。

3 – 在后台队列操作中访问引用计数的variables时,需要根据应用程序中的使用情况使其变为线程安全的。 示例包括写入诸如string,数组等的对象。这些写入应封装在@synchronized关键字中以确保线程安全访问。

@synchronized确保没有其他线程可以访问它所保护的资源,在它封装的块被执行的时候。

 @synchronized(myMutableArray) { //operation } 

在上面的代码块中,任何其他线程都不允许对myMutableArray块中的myMutableArray修改。