CALayer Internals: Contents

It’s right there in the CALayer documentation:

contents
An object that provides the contents of the layer. Animatable.

@property(retain) id contents

Discussion
A layer can set this property to a CGImageRef to display the image as its contents. The default value is nil.

There’s exactly one thing a developer can assign to a layer’s contents property: a CGImageRef. So why is the property declared to be an id?

Back Up a Second.

id is Objective-C’s general object type.  It’s like a void* for objects. We’ve already got kind of a problem here — how is a CGImageRef the same as an Objective-C object? — but short story, Core Foundation pseudo-objects (CFTypes) — and pseudo-objects that derive from CFType (like Core Graphics types) — are set up such that they satisfy the requirements of id. This is a prerequisite for, but not the same as, toll-free bridging. Maybe Jerry will write a post about that in the future!

Anyway.

It’s true that there’s only one thing a developer can write to a layer’s contents, but that’s only half of what a property does. If you read the contents back, you won’t necessarily end up holding a CGImageRef. If the layer has been drawn into, using delegate methods (displayLayer:drawLayer:inContext:) or subclassing (drawInContext:), you’ll actually get an opaque internal type called CABackingStore. This is, as the name implies, a pixel buffer holding the stuff you see in the layer.

Sounds like we have another problem! There’s no header file for CABackingStore; there’s nothing a well-meaning developer can do with it. Or is there? Although the documentation specifies that developers should set layers’ contents to CGImageRefs, they are actually perfectly happy to share generic contents. That means cloning a layer is as easy as layerB.contents = layerA.contents; no cast required, since they’re both type id! (…if they’re both in the same layer hierarchy*, which on iOS they almost certainly will be.)

Takeaway

The documentation doesn’t make it clear, but you can set a CALayer‘s contents property to either a CGImageRef, or the contents of another layer. When querying the contents of a layer, don’t expect to get back a CGImageRef, but do expect something that can serve as the contents of something else. Even if new types (internal or external) are added to the API, this will always hold true.

Joel Kraut

About Joel Kraut

Developing on Apple platforms for, holy shit, over ten years now. Find me on linkedin and twitter. My personal website is foon.us.
This entry was posted in Explanation and tagged , , , . Bookmark the permalink.