11

How can you make an NSArray full of multiple instances of a CALayer (all with the same frame, contents etc)?

Background: CALayer takes a bit of overhead to create, so I would like to create a number of CALayers (all sharing the same properties) in the init method of a class (to be used later on in that class.)

Alex Zavatone
  • 3,885
  • 35
  • 53
Reeds
  • 191
  • 1
  • 2
  • 12
  • Why do you need to copy them? Why not create them all from scratch or, reduce the number of layers your app needs? – Rog Mar 29 '09 at 17:41
  • yes, that's an option (creating them all from scratch), but its not terribly elegant – Reeds Mar 29 '09 at 18:15

6 Answers6

25

I haven't tried this with CALayer specifically, but I know you can perform a deep-copy by taking advantage of NSCoding:

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

I'm not sure how copying them would really help with performance, though.

MaxGabriel
  • 7,527
  • 4
  • 34
  • 80
6

CALayer doesn't have a built in -(id)copy method. I'm not sure why. It's not difficult to gin up your own however. Create a CALayer category and write your own copy method. All you have to do is instantiate and manually get the public ivars/properties from the original and set to the new copy. Don't forget to call [super copy]

BTW, CALayer is an object. You can add it to an NSArray.

amattn
  • 9,945
  • 1
  • 34
  • 33
2

Try to use CAReplicatorLayer. It can duplicate your layers.

reference: https://developer.apple.com/documentation/quartzcore/careplicatorlayer

sample code: http://www.knowstack.com/swift-careplicatorlayer-sample-code/ https://developer.apple.com/documentation/quartzcore/careplicatorlayer/1522391-instancedelay

Alex Zavatone
  • 3,885
  • 35
  • 53
ossamacpp
  • 727
  • 5
  • 11
0

I do exactly the same thing in my program.

In init:

    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];

Basically, just I just repeatedly create CALayer objects. It's going to be faster than copying them, as this method only requires 1 CALayer call per property, as opposed to copying it which requires you to read the property and then additionally set it. I spawn about 500 objects using this method in about 0.02 seconds, so it's definitely fast. If you really needed more speed you could even cache the image file.

Charliehorse
  • 213
  • 2
  • 8
0

NSProxy is used for that reason. What you're describing is a common scenario, and one from which any number of design patterns are derived.

Pro Objective-C Design Patterns for iOS provides the solution to the very problem you describe; read Chapter 3: The Protoype Pattern. Here's a summary definition:

The Prototype pattern specifies the kind of objects to create using a prototypical instance, whereby a new object is created by copying this instan

James Bush
  • 1,455
  • 13
  • 18
0

An updated link to MaxGabriel's top rated answer.

Objective-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];

And in 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. 
}
Alex Zavatone
  • 3,885
  • 35
  • 53