完成块 Objective-C
Posted
技术标签:
【中文标题】完成块 Objective-C【英文标题】:Completion Blocks Objective-C 【发布时间】:2015-03-02 00:27:17 【问题描述】:我正在尝试在 ios 中编写一个带有块的完成处理程序,但不确定它为什么不工作。
这是我目前所拥有的
typedef void(^myCompletion)(BOOL);
-(void) showAnswer
[self showAnswer:^(BOOL finished)
if (finished)
LnbScene *scene = (LnbScene *)gameScene.lnbScene;
//once the tiles position have been updated then move to the next riddle
[scene nextRiddle];
];
// This method should update the position of tiles on the scene
-(void) showAnswer:(myCompletion) compblock
LnbScene *scene = (LnbScene *)gameScene.lnbScene;
NSArray *tilesArray = [scene.tilesBoundary children];
for (Tile *tile in tilesArray)
if (tile.positionInAnswer != 17)
[tile removeFromParent];
[scene.spacesBoundary addChild:tile];
CGPoint targetPoint = CGPointMake(tile.targetPoint.x, tile.targetPoint.y + 6);
tile.position = targetPoint;
compblock(YES);
我正在从场景中调用 showAnswer 方法,如下所示:
GameViewController *gVC = (GameViewController *)self.window.rootViewController;
[gVC showAnswer];
当我逐步执行代码时,我没有遇到任何错误,并且序列按预期进行,即。磁贴位置发生更改,然后完成处理程序触发到 nextRiddle 方法。唯一的问题是没有更新任何界面。有什么我想念的吗?
我已经通过取出完成块测试了磁贴重新定位的代码是否可以工作,我在界面中查看它并获得所需的结果。所以我很确定问题在于我如何实现这些块。我以前从未写过一个块,所以我真的一头雾水。
-(void) showAnswer
LnbScene *scene = (LnbScene *)gameScene.lnbScene;
NSArray *tilesArray = [scene.tilesBoundary children];
for (Tile *tile in tilesArray)
if (tile.positionInAnswer != 17)
[tile removeFromParent];
[scene.spacesBoundary addChild:tile];
CGPoint targetPoint = CGPointMake(tile.targetPoint.x, tile.targetPoint.y + 6);
tile.position = targetPoint;
【问题讨论】:
我不确定我是否理解问题所在。您说“磁贴位置已更改”,但界面未更新。正在改变的磁贴位置不是你想在界面中看到的吗? 对不起 - 我解释得不好。瓷砖不会在界面上更新。我的意思是当我单步执行代码并在调试器中检查它时,一切似乎都可以正常工作,但是当我运行并查看界面时,我看不到所需的结果。下一个谜语更改也不会在界面中生效。 我的猜测是,当您调用showAnswer
方法时,您的 scene
对象为零。你的块代码很好。另一种可能性是您尝试在主线程之外更新 UI,但至少从您迄今为止发布的代码来看,它看起来不像。
【参考方案1】:
我认为这个问题似乎是一个概念上的错误——代码的“流”
在-(void) showAnswer:(myCompletion) comp block
方法中真实地表示了UI元素在时间上的流动在表现层中。
但是在实践中并不是那么简单。 UI 层以 60 Hz 帧速率呈现。这意味着您每秒可以看到 60 个(帧)屏幕,并且每个屏幕的内容都需要提前计算、处理、展平、渲染。
这意味着 1 帧(屏幕)循环/通过持续大约。 16 毫秒。如果我没记错的话,作为一名程序员,你有 11 毫秒的时间来提供所有相关代码,以便将其处理成可视信息。这段代码被一次性处理,这是我们问题的答案。
想象一下,如果你有一个方法会说
//some other UI code...
self.view.backgroundColor = [UIColor greenColor];
self.view.backgroundColor = [UIColor yellowColor];
//some other UI code...
现在假设我们在 1 个这样的 16 毫秒通道内。 这段代码将在任何渲染发生之前预先处理,结果背景颜色将为黄色。为什么?因为在循环准备阶段,此代码被处理并且框架将绿色解释为毫无意义,因为该值立即被黄色覆盖。 因此,渲染器的说明将仅包含黄色背景信息。
现在回到您的实际代码。我认为你所有的代码都被处理得足够快,可以适应那个 11 毫秒的窗口,问题是你并没有真正等待瓷砖的交换,因为有一个带有 YES 参数的块作为方法的一部分,并且一个已经滑动到接下来的任何事情。
因此,渲染确实得到了简单地继续下一件事的指令。而且它没有动画。
尝试将此“瓷砖交换”放入“动画”块中 [UIview animateWithDuration:....completion] 方法..并将您的块放入完成块中。是的,你会有一个街区,但那很好。
[UIView animateWithDuration:0.4f
animations:^
//tile swapping code
completion:^(BOOL finished)
complBlock(YES);
];
我不完全确定它是否会起作用,但让我们试试吧。
【讨论】:
仍在消化以上所有内容。会回复你,看看它是否有效。 这个 WWDC 视频更详细地讨论了它。 developer.apple.com/videos/wwdc/2014/#419 我发现我的代码有问题 - 是 [scene nextRiddle] 方法。下一个 Riddle 方法实例化了一个新的 GameViewController。但我喜欢上面使用 UIView animateWithDuration 的解决方案。我认为它看起来会更聪明。所以谢谢你的帮助。我会看看那个视频【参考方案2】:UI 相关方法应该在主线程上调用。你可以试试这个:
[self showAnswer:^(BOOL finished)
if (finished)
LnbScene *scene = (LnbScene *)gameScene.lnbScene;
dispatch_async(dispatch_get_main_queue(), ^
[scene nextRiddle];
);
];
【讨论】:
以上是关于完成块 Objective-C的主要内容,如果未能解决你的问题,请参考以下文章