On iOS, if a subclass of CALayer is used with an empty display method, then app seems to go into infinite loop?
如果创建了一个单视图应用程序,其 FooView 是 UIView 的子类,然后执行一个
in drawRect,然后打印出来。
如果我创建一个名为 CoolLayer 的 CALayer 子类,并将这个方法添加到 FooView.m:
1 2 3
| +(Class) layerClass {
return [CoolLayer class];
} |
在FooView.m的drawRect的末尾,做一个
1
| NSLog(@"layer's class is %@", self.layer.class); |
然后 CoolLayer 被打印。所以现在视图的底层是 CoolLayer.
但是当将以下内容添加到 CoolLayer.m 时:
这是自动调用以重绘图层的方法(类似于 drawRect),然后没有打印任何 NSLog。可能是应用程序进入了无限循环。 (即使我的 touchesBegan 打印出 NSLog 消息也没有打印)。如果在 display 处设置断点,它会停在那里一次,但是当我继续程序时,它永远不会再到达 display 处。这有什么问题,如何解决?
图层的显示方法不会被再次调用,除非图层是脏的,即设置为需要重新显示。这通常是一件好事,这就是为什么您不会看到该方法被多次调用的原因。
另外,display的正常实现会调用drawInContext:方法。由于您在子类中覆盖了它,因此永远不会调用视图的 drawRect: 方法。您需要复制 CALayer 的标准行为,或者在您自己的实现中调用超类的 display 方法。
- 这真的很奇怪:如果我的 CoolLayer 的 display 是一个全空方法,那些 NSLog 不会被打印出来,而 touchesBegan 不会打印出 NSLog 消息,但是如果我将 [super display]; 添加到那个空的 display 方法,那么所有那些 NSLog 消息都将被打印出来
-
是的,这和预期的一样。有关默认行为的信息,请参阅 Apple 的文档。
-
哦,那是文档中的哪一部分?我的意思是,文档是否有声明说,如果未触摸 contents 或未调用 [super display],那么它将以某种方式挂起或不显示 NSLog 在其他方法中(例如在 UIView 中的 touchesBegan 中)
-
另一个更新是,如果 [super display]; 被删除,但添加了一个 self.contents = (id) [uiimage CGImage];,那么图像将被显示,并且 NSLog 消息将在 FooView\\ 的 touchesBegan 方法中打印......但正在触摸或设置contents 强制?文档没有提到它必须设置....
-
好的,我想我找到了未打印 touchesBegan 中的 NSLog 消息的原因:没有什么可触摸的。或者大小可能就像 (0,0),没有任何东西被触及,并且 touchesBegan 从未被调用过。但是我不遵循如果 display 为空,则永远不会调用 drawRect 的逻辑?我将 NSTimer 设置为每 1 秒重复一次到 [self.view setNeedsDisplay];,并调用 display,但不调用 drawRect
-
请参阅 display 方法的"讨论"段落。它说明了默认行为(例如,您必须重新实现或以其他方式在子类中复制的行为),并提供有关如何实现它的建议。
-
它是否提到在某些情况下不调用drawRect?我搜索了那个文档,它根本没有提到 drawRect ......另外,我怀疑 display 这样做:它可以将 contents 设置为 CGImage 对象,或者它启动一个 ImageContext,并通过它ImageContext 到 drawInContext,然后当它返回时,将该 ImageContext 转换为 CGImage,并将其设置为 contents ...我希望一些文档可以验证这一点,还有,为什么即使 NSTimer 设置了 drawRect 现在也不调用使用 setNeedsDisplay 查看?
这听起来不像是无限循环。如果您处于无限循环中,您的应用程序将冻结,几秒钟后,跳板应用程序会因无响应而终止它。
在您的图层上调用 setNeedsDisplay 或 setNeedsDisplayInRect 以使其"脏"并要求再次绘制。请注意,您不必再调用 setNeedsDisplay,因为重新渲染图层并将其内容推送到屏幕上需要大量工作。仅在某些内容发生更改时显示
您在这里没有看到无限循环。如果你是,正如 Duncan 指出的那样,你最终会因为看门狗定时器或无限递归而崩溃,这在你在调试器中看到的堆栈跟踪中会立即显而易见。
如果您将 NSLog 放入 UIView 的 -drawRect: 方法中,然后使用您自己的自定义 CALayer 覆盖其默认图层类,该 CALayer 会进行自己的绘图,您的 UIView 的 -drawRect: 将不再被调用。绘图现在将由您的自定义支持层处理。
如核心动画编程指南的"通过子类化提供 CALayer 内容"小节中所述,如果您想以某种方式自定义图层的 contents CGImageRef,您通常会在 CALayer 中覆盖 -display。通常,如果您想在 CALayer 中渲染自定义 Quartz 绘图,您将覆盖 -drawInContext:。使您被覆盖的 -display 方法完全空白,并且不向 contents 属性写入任何内容,这不是标准行为,所以我对您看到这样做的奇怪结果并不感到惊讶。
根据您最近提出的一系列问题,我强烈建议您停下来花一些时间阅读 Core Animation Programming Guide 并查看一些涉及 CALayers 的示例代码,然后再继续。
-
我想我找到了奇怪的原因:没有设置图像,也没有什么可触摸的......但在我在这里看到你的答案之前,我发布了另一个问题,可能描述了 display 和 drawInContext 的工作原理。 .但不确定它如何不会调用drawRect:stackoverflow.com/questions/10791025/...。我想在我理解之后,我对 CALayer 如何缓存图像、setNeedsDisplay 如何真正工作以及如何调用 drawRect 有了一个很好的了解