Why updating CALayer causes increase of CPU usage

2019-08-17 13:08发布

I am witing IOS app when interface is presented by many CALayers. Once I noticed that CPU is loaded by other processes (not app actually) during graphics updates. I began to disabled updating of interface parts and went to the moment when only ONE CALayer was updated (at 50-60 Hz), but all other layers (hundreds) were statically displayed too. So updating of only this ONE layer costs 60-70% of CPU load by other processes. When disable update of this only layer, CPU is not loaded.

Can anyone say what is going on here??

[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];

// update layer which is sublayer of self.view.layer
self.headingCircularScaleLayer.transform = CATransform3DMakeRotation(DegToRad(-newHeadingAngle_deg), 0, 0, 1);

[self.view.layer setNeedsLayout];

[CATransaction commit];

Here headingCircularScaleLayer is CALayer which contents is set to some Image

NOTE_1: (test of CPU load by adding many layers and updating them)

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

-(void) viewDidAppear:(BOOL)animated {

    [self createAndSetupLayers];

    CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateLayers)];
    [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];

    self.startTime = [[NSDate date] timeIntervalSince1970];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


-(void) createAndSetupLayers {

    NSMutableArray<CALayer*>* newLayersArray = [[NSMutableArray alloc] init];

    long numOfRows = 28;
    long numOfCols = 21;
    long numOfLayers = numOfRows * numOfCols;

    CGFloat cellWidth = self.view.bounds.size.width / numOfCols;
    CGFloat cellHeight = self.view.bounds.size.height / numOfRows;

    CGFloat layerWidth = cellWidth * 0.9;
    CGFloat layerHeight = cellHeight * 0.9;

    long currRow = 0;
    long currCol = 0;

    for (long i = 0; i < numOfLayers; i++)
    {
        currRow = i / numOfCols;
        currCol = i % numOfCols;

        CALayer* newLayer = [[CALayer alloc] init];

        newLayer.bounds = CGRectMake(0.0, 0.0, layerWidth, layerHeight);
        newLayer.anchorPoint = CGPointMake(0.5, 0.5);
        newLayer.position = CGPointMake((currCol + 0.5) * cellWidth, (currRow + 0.5) * cellHeight);
        newLayer.backgroundColor = [UIColor greenColor].CGColor;
        //newLayer.opacity = 0.5;

        //NSDictionary *newActions = @{ @"transform": [NSNull null] };
        //newLayer.actions = newActions;
        newLayer.actions = @{ @"contents": [NSNull null], @"position": [NSNull null],
                           @"frame": [NSNull null], @"opacity": [NSNull null],
                           @"bounds": [NSNull null], @"affineTransform": [NSNull null],
                           @"sublayerTransform": [NSNull null], @"transform": [NSNull null],
                           @"zPosition": [NSNull null], @"anchorPoint": [NSNull null],
                           @"cornerRadius": [NSNull null], @"sublayers": [NSNull null],
                           @"onLayout": [NSNull null], };


        CALayer* newColorLayer = [[CALayer alloc] init];
        newColorLayer.bounds = CGRectMake(0.0, 0.0, layerWidth, layerHeight);
        newColorLayer.anchorPoint = CGPointMake(0.5, 0.5);
        newColorLayer.position = CGPointMake(0.5 * layerWidth, 0.5 * layerHeight);
        newColorLayer.opacity = 0.5;
        newColorLayer.opaque = YES;
        newColorLayer.backgroundColor = [UIColor redColor].CGColor;

        //[newLayer addSublayer:newColorLayer];
        [self.view.layer addSublayer:newLayer];
        [newLayersArray addObject:newLayer];
    }

    self.layersArray = newLayersArray;
}

-(void) updateLayers {
    double currTime = [[NSDate date] timeIntervalSince1970] - self.startTime;

    double currLayerAngle = 0.5 * currTime;

    NSLog(@"%.2f  %.2f", currTime, currLayerAngle);

    //[CATransaction begin];
    //[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
    //[CATransaction setAnimationDuration:0.0];

    for (CALayer *currLayer in self.layersArray)
    {
        currLayer.transform = CATransform3DMakeRotation(currLayerAngle, 0, 0, 1);
    }

    //[CATransaction commit];
}

@end

1条回答
男人必须洒脱
2楼-- · 2019-08-17 13:50

As said in the comments above, setting kCATransactionDisableActions will only affect the current transaction. You might have code in other places that starts implicit animations. Try explicitly setting the layer actions to null. You only need to do this once, when the layer is created. In this case you don't need to do explicit transactions.

layer.actions = @{ @"contents": [NSNull null], @"position": [NSNull null],
                   @"frame": [NSNull null], @"opacity": [NSNull null],
                   @"bounds": [NSNull null], @"affineTransform": [NSNull null],
                   @"sublayerTransform": [NSNull null], @"transform": [NSNull null],
                   @"zPosition": [NSNull null], @"anchorPoint": [NSNull null],
                   @"cornerRadius": [NSNull null], @"sublayers": [NSNull null],
                   @"onLayout": [NSNull null], };
查看更多
登录 后发表回答