wantsLayer 的 NSView 性能
NSView performance of wantsLayer
如果我创建一个空白 Mac XCode 项目并在主 window 中并排布置 500 个简单 NSView
对象,它加载速度非常快。如果我在每个子视图上设置 wantsLayer=YES
,性能会急剧下降几秒钟。为什么在概念上会出现这种情况?看起来层会比常规的旧 NSViews 更快而不是更慢。
您通过对如此多的视图进行分层支持,给了系统更多的工作要做。 Layer-backing 允许图形加速(用于绘图),但它会增加一些开销,比如布局,更不用说 只是创建它们并将它们放在屏幕上 。如果使用得当,问题不大。
通常,如果您同时在屏幕上管理这么多 "things",您将拥有一个管理其自己的子层树的层支持的托管视图。 "But what about view-based table views?" 你问。诡计,诡计,我说! Table 视图实际上并没有保留它们管理的所有单元格视图;他们有效地重复使用它们,只保留足以代表屏幕上的内容 and/or 动画。
所以我想说这不是一个真正的问题,因为它不是一个特别好的方法来抛出 500 多个图层支持的视图来开始布局和绘图。 :-)
截至 2021 年,Joshua Nozzi 的回答只对了一半。
当你想使用那么多层时,你应该利用 CALayers 和子层的强大功能,而不是一遍又一遍地使用 NSViews,每个层都有自己的 NSCell 和可能的 CALayer,因为你强迫它使用 -wantsLayer:
.
500个子层没有问题。子层在结构合理的情况下可以让你更快。
如果你想加快速度,在不需要的每个图层上强制关闭自动动画。是的,很多例子表明,每次调用 属性 进行更改时,您都可以关闭自动动画,这会进一步减慢您的绘图速度,因为它涉及更多的对象消息传递。
请记住以下示例通过按键名称关闭自动动画按键并踢出整个动作,并且不会故意应用于所有子层。如果你的 Sublayers of Sublayers 结构需要它,你可以根据 ParentLayer 调用它来消除 CPU eating Autoanimation。最好让自动动画在您真正想要的图层上完成它的工作。 这改变了 Apple 的默认范例,使所有内容都具有动画效果。 如果关闭了大多数操作键,请注意您的层确实显示得有点像状态机。在此处的示例中 @"frame"
动画未被清除..
void turnOffAutoAnimationsForLayerAndSublayers(CALayer* layer) {
NSNull *no = [NSNull null]; //(id)kCFNull
NSDictionary *dict = [NSDictionary dictionaryWithObjects:@[no,no,no,no,no,no,no,no]
forKeys:@[@"bounds",@"position",@"contents",@"hidden",@"backgroundColor",@"foregroundColor",@"borderWidth",@"borderColor"]];
for (CALayer *layertochange in layer.sublayers ) {
if (layertochange.sublayers.count) {
for (CALayer *sublayertochange in layertochange.sublayers) {
sublayertochange.actions = dict;
}
}
layertochange.actions = dict;
}
}
并在 CALayer
支持的 NSView
-init 中使用它,例如..
CALayer *layer = [CALayer layer];
layer.frame = self.frame;
//...backgroundColor, contents, etc etc..
// here you could even do a for loop adding as much sublayers you want..
for (int y=0; y<100; y++) {
CALayer *sub = [CALayer layer];
//...frame, backgroundColor, contents, of sublayers etc etc..
layer.frame = CGRectMake(0, 0, 10, y*10);
[layer addSublayer:sub];
}
// structure is ready, add it to the base layer
self.layer = layer;
// now tell NSView you really want to make use this layer structure
self.wantsLayer = YES;
// turn off AutoAnimations
turnOffAutoAnimationsForLayerAndSublayers(self.layer);
对于基本相同的层,甚至还有一个专门的子类,称为 CAReplicatorLayer
。允许您使用更少的内部绘制调用添加更多内容。
你还应该知道,隐藏 的图层根本不会计算(意思是在这里绘制)。即使在隐藏层上,您仍然可以更改属性。在需要时取消隐藏它们,而不是更早。因此,在示例中,自定义 CATextLayer
将其 字符串 属性 更改为 @""
,也就是什么都不会绘制。但是,如果您将 CATextLayer 子类化并将其字符串实现更改为让我们说..
@interface YourTextAutoHideLayer : CATextLayer
-(void)setString:(id _Nullable )string;
@end
@implementation YourTextAutoHideLayer
-(void)setString:(id _Nullable)string {
self.hidden = [string isEqual:@""];
super.string = string;
}
@end
你的绘图速度更快了。 A) 因为更少的对象消息来隐藏没有文本的图层,并且 B) 一旦隐藏它就不是内部绘制调用的一部分有效加速你的主要 CALayers 绘图。
在基于 NSCell 的 NSTableView 上,您通常从不使用 CALayers。没有必要,NSTableViews 有意管理可见单元格以保持示例的平滑滚动。而且它们 (NSTableViewCells) 仍然有一个重用机制来加快速度。无论如何,你很可能不会用 CALayers 重新发明一个 tableview。
如果这还不够快,则值得考虑使用 GPU 强大功能的 MetalView。
如果我创建一个空白 Mac XCode 项目并在主 window 中并排布置 500 个简单 NSView
对象,它加载速度非常快。如果我在每个子视图上设置 wantsLayer=YES
,性能会急剧下降几秒钟。为什么在概念上会出现这种情况?看起来层会比常规的旧 NSViews 更快而不是更慢。
您通过对如此多的视图进行分层支持,给了系统更多的工作要做。 Layer-backing 允许图形加速(用于绘图),但它会增加一些开销,比如布局,更不用说 只是创建它们并将它们放在屏幕上 。如果使用得当,问题不大。
通常,如果您同时在屏幕上管理这么多 "things",您将拥有一个管理其自己的子层树的层支持的托管视图。 "But what about view-based table views?" 你问。诡计,诡计,我说! Table 视图实际上并没有保留它们管理的所有单元格视图;他们有效地重复使用它们,只保留足以代表屏幕上的内容 and/or 动画。
所以我想说这不是一个真正的问题,因为它不是一个特别好的方法来抛出 500 多个图层支持的视图来开始布局和绘图。 :-)
截至 2021 年,Joshua Nozzi 的回答只对了一半。
当你想使用那么多层时,你应该利用 CALayers 和子层的强大功能,而不是一遍又一遍地使用 NSViews,每个层都有自己的 NSCell 和可能的 CALayer,因为你强迫它使用 -wantsLayer:
.
500个子层没有问题。子层在结构合理的情况下可以让你更快。
如果你想加快速度,在不需要的每个图层上强制关闭自动动画。是的,很多例子表明,每次调用 属性 进行更改时,您都可以关闭自动动画,这会进一步减慢您的绘图速度,因为它涉及更多的对象消息传递。
请记住以下示例通过按键名称关闭自动动画按键并踢出整个动作,并且不会故意应用于所有子层。如果你的 Sublayers of Sublayers 结构需要它,你可以根据 ParentLayer 调用它来消除 CPU eating Autoanimation。最好让自动动画在您真正想要的图层上完成它的工作。 这改变了 Apple 的默认范例,使所有内容都具有动画效果。 如果关闭了大多数操作键,请注意您的层确实显示得有点像状态机。在此处的示例中 @"frame"
动画未被清除..
void turnOffAutoAnimationsForLayerAndSublayers(CALayer* layer) {
NSNull *no = [NSNull null]; //(id)kCFNull
NSDictionary *dict = [NSDictionary dictionaryWithObjects:@[no,no,no,no,no,no,no,no]
forKeys:@[@"bounds",@"position",@"contents",@"hidden",@"backgroundColor",@"foregroundColor",@"borderWidth",@"borderColor"]];
for (CALayer *layertochange in layer.sublayers ) {
if (layertochange.sublayers.count) {
for (CALayer *sublayertochange in layertochange.sublayers) {
sublayertochange.actions = dict;
}
}
layertochange.actions = dict;
}
}
并在 CALayer
支持的 NSView
-init 中使用它,例如..
CALayer *layer = [CALayer layer];
layer.frame = self.frame;
//...backgroundColor, contents, etc etc..
// here you could even do a for loop adding as much sublayers you want..
for (int y=0; y<100; y++) {
CALayer *sub = [CALayer layer];
//...frame, backgroundColor, contents, of sublayers etc etc..
layer.frame = CGRectMake(0, 0, 10, y*10);
[layer addSublayer:sub];
}
// structure is ready, add it to the base layer
self.layer = layer;
// now tell NSView you really want to make use this layer structure
self.wantsLayer = YES;
// turn off AutoAnimations
turnOffAutoAnimationsForLayerAndSublayers(self.layer);
对于基本相同的层,甚至还有一个专门的子类,称为 CAReplicatorLayer
。允许您使用更少的内部绘制调用添加更多内容。
你还应该知道,隐藏 的图层根本不会计算(意思是在这里绘制)。即使在隐藏层上,您仍然可以更改属性。在需要时取消隐藏它们,而不是更早。因此,在示例中,自定义 CATextLayer
将其 字符串 属性 更改为 @""
,也就是什么都不会绘制。但是,如果您将 CATextLayer 子类化并将其字符串实现更改为让我们说..
@interface YourTextAutoHideLayer : CATextLayer
-(void)setString:(id _Nullable )string;
@end
@implementation YourTextAutoHideLayer
-(void)setString:(id _Nullable)string {
self.hidden = [string isEqual:@""];
super.string = string;
}
@end
你的绘图速度更快了。 A) 因为更少的对象消息来隐藏没有文本的图层,并且 B) 一旦隐藏它就不是内部绘制调用的一部分有效加速你的主要 CALayers 绘图。
在基于 NSCell 的 NSTableView 上,您通常从不使用 CALayers。没有必要,NSTableViews 有意管理可见单元格以保持示例的平滑滚动。而且它们 (NSTableViewCells) 仍然有一个重用机制来加快速度。无论如何,你很可能不会用 CALayers 重新发明一个 tableview。
如果这还不够快,则值得考虑使用 GPU 强大功能的 MetalView。