【问题标题】:How do you copy a CALayer?你如何复制一个CALayer?
【发布时间】:2026-02-06 16:30:01
【问题描述】:

如何使 NSArray 充满 CALayer 的多个实例(都具有相同的框架、内容等)?

背景:创建 CALayer 需要一些开销,所以我想在一个类的 init 方法中创建一些 CALayer(都共享相同的属性)(稍后在该类中使用。)

【问题讨论】:

  • 为什么需要复制它们?为什么不从头开始创建它们,或者减少应用所需的层数?
  • 是的,这是一个选项(从头开始创建它们),但它不是非常优雅

标签: iphone objective-c core-animation calayer


【解决方案1】:

我没有专门用CALayer 尝试过这个,但我知道你可以利用NSCoding 执行深层复制:

CALayer *layer = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:layer]];

不过,我不确定复制它们对性能有何帮助。

【讨论】:

  • 这是给你的更新。再次感谢。 *.com/a/68006776/1058199
【解决方案2】:

CALayer 没有内置的-(id)copy 方法。我不确定为什么。然而,自己动手并不难。创建一个 CALayer 类别并编写自己的复制方法。您所要做的就是实例化并手动从原始版本中获取公共 ivars/属性并设置为新副本。别忘了拨打[super copy]

顺便说一句,CALayer 是一个对象。您可以将其添加到 NSArray。

【讨论】:

    【解决方案3】:

    【讨论】:

    • 超级有帮助。比最佳答案更好。
    【解决方案4】:

    我在我的程序中做同样的事情。

    在初始化中:

        self.turrets = [NSMutableArray array];
        for (count = 0; count < kMaxTurrets; count++)
            [self spawnTurret];
    

    spawnTurret:

    evTurret* aTurret = [[[evTurret alloc] init] autorelease];
    CGImageRef theImage = [self turretContents];
    aTurret.contents = theImage;
    double imageHeight = CGImageGetHeight(theImage);
    double imageWidth = CGImageGetWidth(theImage);
    double turretSize = 0.06*(mapLayer.bounds.size.width + mapLayer.bounds.size.height)/2.0;
    aTurret.bounds = CGRectMake(-turretSize*0.5, turretSize*0.5, turretSize*(imageWidth/imageHeight), turretSize);
    aTurret.hidden = YES;
    [mapLayer addSublayer:aTurret]; 
    [self.turrets addObject:aTurret];
    

    基本上,我只是重复创建 CALayer 对象。这将比复制它们更快,因为此方法只需要每个属性调用 1 次 CALayer,而不是复制它,这需要您读取属性然后另外设置它。我使用这种方法在大约 0.02 秒内生成了大约 500 个对象,所以它肯定很快。如果你真的需要更快的速度,你甚至可以缓存图像文件。

    【讨论】:

    • 这并没有真正的帮助。问题是关于复制或复制图层。
    【解决方案5】:

    NSProxy 就是因为这个原因而使用的。您所描述的是一种常见场景,并且可以从中派生出任意数量的设计模式。

    iOS 的 Pro Objective-C 设计模式为您描述的问题提供了解决方案;阅读第 3 章:原型模式。这是一个摘要定义:

    原型模式指定使用原型实例创建的对象类型,通过复制此实例来创建新对象

    【讨论】:

      【解决方案6】:

      MaxGabriel 评分最高的答案的更新链接。

      目标-C

      CALayer *layer1;
      CALayer *layer2;
      
      // Set up layer1's specifics.
      
      layer2 = [NSKeyedUnarchiver unarchivedObjectOfClass:[CALayer class]
                                                                  fromData:[NSKeyedArchiver archivedDataWithRootObject:layer1 requiringSecureCoding:NO error:nil] error:nil];
      

      在 Swift 中。

      let layer1: CALayer?
      var layer2: CALayer? = nil
      // Set up layer1's specifics
      
      do {
          layer2 = try NSKeyedUnarchiver.unarchivedObject(
              ofClass: CALayer.self,
              from: try NSKeyedArchiver.archivedData(withRootObject: layer1, requiringSecureCoding: false))
      } catch {
      // It failed.  Do something. 
      }
      

      【讨论】: