My class contains an UIImage property which I want to enforce as a \'copy\' property by any external clients accessing it. But, when I try to do a copy in my custom setter,
CGImageRef newCgIm = CGImageCreateCopy(oldImage.CGImage);
UIImage *newImage = [UIImage imageWithCGImage:newCgIm scale:oldImage.scale
orientation:oldImage.imageOrientation];
Code for Swift 5.1:
let imageToCopy = <# your_image #>
UIGraphicsBeginImageContext(imageToCopy.size)
imageToCopy.draw(in: CGRect(x: 0, y: 0, width: imageToCopy.size.width, height: imageToCopy.size.height))
let copiedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
When talking about deep copies we must first understand that UIImage
is a container. It doesn't actually contain the image data. The underlying data can be a CIImage
or a CGImage
. In most cases the backing data is a CGImage
which is in turn a wrapping struct and copying the CGImage
is just copying the metadata and not the underlying data. If you want to copy the underlying data you can draw the image into a context or grab a copy of the data as a PNG.
Creating a playground with the following demonstrates the method.
let zebra = UIImage(named: "an_image_of_a_zebra")
print(zebra?.CGImage) // check the address
let shallowZebra = UIImage(CGImage: zebra!.CGImage!)
print(shallowZebra.CGImage!) // same address
let zebraData = UIImagePNGRepresentation(zebra!)
let newZebra = UIImage(data: zebraData!)
print(newZebra?.CGImage) // new address
Because the image itself is a pointer, you need to create an image context, draw the image into the context, and then get the original image from the context. Maybe you need to use the UIImage.CGImage.
Why do you need copy semantics? The UIImage is immutable, so there's no benefit to setting a copy policy. You just need a copy policy if there's the risk that someone else could modify the image on you. Since the UIImage is immutable, there's no risk of that happening, so a retain property is fine.
If you explain a bit more what you're trying to do, it might be that there's some other way of achieving it.
Here is a way to do it:
UIImage *imageToCopy = <# Your image here #>;
UIGraphicsBeginImageContext(imageToCopy.size);
[imageToCopy drawInRect:CGRectMake(0, 0, imageToCopy.size.width, imageToCopy.size.height)];
UIImage *copiedImage = [UIGraphicsGetImageFromCurrentImageContext() retain];
UIGraphicsEndImageContext();
The reason I needed this is that I had an image variable initialized with imageWithContentsOfFile:, and I wanted to be able to delete the file from disk but keep the image variable. I was surprised to find that when the file is deleted the image variable goes crazy. It was displaying wacky random data even though it was initialized before the file was deleted. When I deep copy first it works fine.