I'm using CGDataProviderCreateWithData to (eventually) create a UIImage from a malloced array of bytes. I call CGDataProviderCreateWithData like this:
provider = CGDataProviderCreateWithData(NULL, dataPtr, dataLen, callbackFunc);
where
dataPtr is the previously malloced array of data bytes for the image,
dataLen is the number of bytes in the dataPtr array, and
callbackFunc is as described in the CGDataProviderCreateWithData documentation:
void callbackFunc(void *info, const void *data, size_t size);
The callback function is called when the data provider is released so I could free() dataPtr there, but I may want to continue using it (dataPtr) and at some later stage free it. This block of code will be called multiple times, and the flow will look something like:
malloc(dataPtr)
create image (call CGDataProviderCreateWithData etc)
display image
release image (and so release data provider created by CGDataProviderCreateWithData)
continue to use dataPtr
free(dataPtr)
so 1..6 may be executed multiple times. I don't want dataPtr hanging around for the entire execution of the program (and it may change in size anyway), so I want to malloc/free it as necessary.
The problem is that I can't free(dataPtr) in the callback from CGDataProviderCreateWithData because I still want to use it, so I want to free it some time later - and I can't free it until I know that the data provider no longer needs it (as far as I can tell CGDataProviderCreateWithData uses the array I pass, it doesn't take a copy).
I can't do (1) above until I know it is ok to free and re-malloc dataPtr, so what I really want to do is block waiting for the data provider to be freed (well, I want to know whether I should re-enter the 1..6 block of code, which I can't do until the data provider is freed). It will be - I create the data provider, create the image and immediately display it and release the data provider. The trouble is that the data provider isn't actually released until the UIImage is released and is finished with it.
I'm reasonably new to objective-c and iOS. Am I missing something obvious?
If you malloc the memory for the provider's data, you really want to to free it in the callback. Do not be tempted to try to circumvent this in any manner. It be too easy to leak and would be susceptible to simple memory issues.
Having said that, there are two simple solutions that address your question. Either:
Make your own copy of the data that you'll manage separately from the provider; or
Instead of using the void * renditions of the CGDataProvider methods, use the CFData rendition (e.g. CGDataProviderCreateWithCFData) and then you can maintain your own strong reference to this data object (or if in non-ARC code, do your own retain of the data object). The object will not be deallocated until all strong references are resolved (or, in non-ARC code, all of your manual retain calls are resolved with a corresponding release or autorelease call).
With either of these approaches, you can continue to let CGProviderRef manage the memory as it sees fit, but you can continue to use the data object for your own purposes, too.
I ran into a similar problem as well. I wanted to use CGImageCreateWithJPEGDataProvider, so used CGDataProviderRef with a malloc'd byte array. I was getting the same problem, when trying to free the byte array at some point after creating the Data Provider Reference.
It occurs because the data provider reference takes over the ownership of the byte array, so can only be freed by the reference, when it is done with it.
I agree with #TheBlack in that if you need the data elsewhere, make a copy of it.
What I'm doing is essentially the same to what you want to achieve, I just took different route.
This whole process is wrapped into NSOperation so you have control over scheduling memory usage.
CGImageRef imageRef = [image CGImage];
NSUInteger width = CGImageGetWidth(imageRef);
NSUInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageRef);
NSUInteger bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
UInt8 *rawData = calloc((height * width * bitsPerComponent), sizeof(UInt8));
NSUInteger bytesPerRow = CGImageGetBytesPerRow(imageRef);
CGContextRef context = CGBitmapContextCreate(rawData, width, height,
bitsPerComponent, bytesPerRow, colorSpace,
kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
if (context)
{
CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef);
// This is the place you get access to data trough rawData. Do whatever you want with it, when done, get image with
CGImageRef newImg = CGBitmapContextCreateImage(context);
}
OR
subclass CALayer, override drawInContext and use CGBitmapContextGetData to get raw data. Don't have much experience with this though, sorry, but if you want to see instant changes
on image based on user input, I'd do it this way:
Subclass UIView and CALayer which becomes layer for view and displays image. View gets input and image data (from CGBitmapContextGetData) in layer class is manipulated based on input.
Even CATiledLayer can be used for huge images in this way. When done with image, just release UIView and replace it with new one. I'll gladly provide help if you need any, just ping here.
Related
CMSampleBufferRef sampleBuffer = [assetOutput copyNextSampleBuffer];
CMBlockBufferRef buffer = CMSampleBufferGetDataBuffer(sampleBuffer);
CMBlockBufferAppendBufferReference(_sem == 0 ? _buffer0 : _buffer1, buffer, 0, 0, 0);
//if(sampleBuffer)
// CFRelease(sampleBuffer);
Why does this cause a memory leak at the first line (at least that's where Leaks suggests)? I have my assetOutput.shouldAlwaysCopySampleOutput = NO. Here's my understanding of the situation:
CMSampleBufferRef sampleBuffer = [assetOutput copyNextSampleBuffer];
This line will create a reference to the sample buffer from the assetOutput.
CMBlockBufferRef buffer = CMSampleBufferGetDataBuffer(sampleBuffer);
This line will get the CMBlockBuffer from the CMSampleBuffer but will not allocate a new buffer, and the Get method in this case means it is a temporary (autoreleased) buffer
CMBlockBufferAppendBufferReference(_sem == 0 ? _buffer0 : _buffer1, buffer, 0, 0, 0);
This line will append the reference of the CMBlockBuffer created above, to the selected global-scope buffer. It will not copy any memory blocks.
So in none of these three lines do I allocate any memory nor do I copy any memory, it's all references. I don't understand where the leak is coming from. I tried adding the commented out lines and it still seems to leak (although fewer times)
alwaysCopiesSampleData is not about memory management. It is only about whether you are scribbling on the original sample buffer or a clone of the original. It is somewhat unfortunately named.
copyNextSampleBuffer follows the create rule and as such, should be released when you are done with it. It creates a reference with a retain count of at least 1.
The Create Rule:
https://developer.apple.com/library/ios/DOCUMENTATION/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html#//apple_ref/doc/uid/20001148-103029
Apple's doc links tend to change, but if the above link dies, just google "The Create Rule"
Core Foundation data structures follows the same ownership rules as Foundation objects.
The rules are pretty simple - whatewer you create (or get the ownership by some other way), you have to destroy. If some other method wants to work with the same structure/object, it has to ask for the ownership and thus preventing the destruction.
Taking ownership = "create" / "retain"
Releasing ownership ("destruction") = "release"
In your sample code, you have created a structure using copyNextSampleBuffer. That means, you have to also destroy it using CFRelease.
(Note that with ARC you don't actually see the retain and release calls but with Core Foundation, you have to use them explicitely).
I'm trying to load an image from a url inside of drawRect:. Our image-loading code is a method that looks like this: - (void) loadImage:(NSURL*)url done:(void(^)(UIImage*))done;, which creates an asynchronous NSURLConnection and calls back with the image.
So, my code in drawRect: looks like this:
CGContextRef context = UIGraphicsGetCurrentContext()
[service loadImage:url done:^(UIImage * image){
CGContextDrawImage(context, frame, image.CGImage);
}];
Unfortunately, this doesn't work. The image is never drawn.
I've also tried using synchronous connections ([NSData dataWithContentsOfUrl:]), but it blocks the thread and slows things down unnecessarily. I don't want to use a UIImageView.
What is the correct way to do this? Thanks!
I think the correct way would either be to load the image beforehand or draw the image as soon as you receive it by either calling "setNeedsDisplayInRect:" or doing your own drawing handling in a separate method (iOS might discourage you from doing this though, I am not sure). Obviously the setNeedsDisplayInRect variation would require you to have the image at hand for drawing the second time around (cache it).
If you load the image asynchronously your drawRect method will moste likely have exited before the image is loaded and the draw focus is no longer locked on your drawRect, thus making it impossible to draw the image in the context. Did you try to set a breakpoint to verify this though? I might be wrong about the locking, but thought I read something alike this at some point.
Think it through--why would it run? There's no guarantee that context is still the current context (or even that its backing store is still fully valid) by the time the block is entered. By using an asynchronous callback, be it a block or a delegate method or something else, you're intentionally deferring code to a later point in time. The call to -drawRect: has ended and is off the stack long before your block is entered.
The first thing I would try is to make sure the async handler runs on the main thread:
[service loadImage:url done:^(UIImage * image) {
dispatch_async(dispatch_get_main_queue(), ^{
CGContextDrawImage(context, frame, image.CGImage);
});
}];
If that does not help, I would start to wonder if the context is still valid. The pointer is, but the context probably not. Maybe you could move the context acquisition to the handler:
[service loadImage:url done:^(UIImage * image) {
dispatch_async(dispatch_get_main_queue(), ^{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextDrawImage(context, frame, image.CGImage);
…
});
}];
Not sure if that makes sense API-wise, I don’t do much stuff like this.
Have you tried:
__block CGContextRef context = UIGraphicsGetCurrentContext()
[service loadImage:url done:^(UIImage * image){
CGContextDrawImage(context, frame, image.CGImage);
}];
You may have to do the same thing with frame, so when the block returns it knows what context and frame it should modify.
I'm creating an bitmap context, and in my code there is this:
bitmapData = malloc(bitmapByteCount);
context = CGBitmapContextCreate (bitmapData,
pixelsWidth,
pixelsHeight,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedFirst);
before the method returns the CGContextRef object, I think I must release the bitmapData. Can I safely call free(bitmapData) before I return the context?
The documentation for CGBitmapContextCreate says this:
In iOS 4.0 and later, and Mac OS X
v10.6 and later, you can pass NULL if
you want Quartz to allocate memory for
the bitmap. This frees you from
managing your own memory, which
reduces memory leak issues.
I would suggest you pass NULL instead of a malloc'd pointer and you will be free of worrying about its memory.
However, be mindful that CGBitmapContextCreate has 'create' in its name, so by convention you will own the object returned. You will need to release this at some point with CFRelease().
Jasarien's answer is best if you're developing for iOS version 4.0 or newer. If you want to support older versions, then keep reading.
You have to keep the bitmapData around as long as the context is being used. If you try to draw into the bitmap context and you've freed bitmapData, Bad Things will happen. The best solution is to free bitmapData after you call CFRelease on the context. If you called CGBitmapContextCreateImage to extract a CGImage from the bitmap context then don't worry... when you release the bitmap context, the CGImage will make its own copy of the bitmap data.
What this means is that making a method or function that creates and returns a bitmap context might not be the greatest idea. If you can, it would be best to create the context at the top of the method, use it in that methpd, and then release the context and free the bitmap at the end of the method. If you can't do that, consider storing the context and its bitmapData in ivars. If you need multiple bitmap contexts at one time, you'll probably want to create an object to track the context and its bitmapContext.
This is why it's best to pass NULL for the bitmapData if you're only supporting iOS version 4.0 or newer. If you're on 4.0+ and pass NULL, you can safely ignore the stuff I said above and just make sure that the caller eventually calls CFRelease on the context you return.
I am getting EXEC_BAD_ACCESS errors in a CGContextDrawImage call and trying to trace it back.
I have a PNG image and running a UIImagePNGRepresentation o it gives me NSData instance.
I need to convert this to CGImageRef instance, so I can run the CGImageCreateWithPNGDataProvider method with this CGImageRef.
I tried two ways:
1) To cast it.
CGImageRef ref = (CGDataProvider)nsdata;
2) To run CGDataProviderCreateWithCFData(nsdata);
First case returns empty image, though command doesnt fail.
The reason I tried the second case, even though I have NSData and not CFData is because I remember reading it accepts both.
Whatever be the reason, it is failing due to this.
Is there a way I can use my PNG NSData to create a CGImage that is not corrupted?
Please help.
THanks
Your second try is almost right. CFData and NSData are “toll-free bridged”. Anything that accepts a CFDataRef also accepts NSData (and vice-versa) — you just have to cast correctly.
You need:
CGDataProviderCreateWithCFData((CFDataRef)myNSData);
The first is very wrong. You can not turn an NSData instance into a CGImageRef simply by casting it.
The second should work fine. You will have to cast the NSData instance to a CFDataRef but that is perfectly legal due to what Apple calls toll-free bridging.
Here is another and much easier method:
NSData* data = ... get raw image data from somewhere (PNG, JPEG, etc.) ...;
UIImage* image = [UIImage imageWithData: data];
CGImageRef imageRef = image.CGImage;
I prefer to use the higher level UIImage methods to load images and then use image.CGImage for lower level Core Graphics functions. Don't forget to properly retain the UIImage if you need to keep it around.
Does UIImage ever removes images from its cache? Can I keep a pointer to an image I got from imageNamed: and use it as long as I like or must I always call imageNamed:?
The UIImage object that is returned from imageNamed: is treated like all other objects as far a memory management goes. If you want to keep the reference to the object between method calls, you should retain it and release it when you are done to decrement the reference count.
UIImage * cachedImage;
-(void) getTheImage {
UIImage * cachedImage = [[UImage imageNamed:#"MyImage.png"] retain];
//Do something with the image...
}
//In some other method or dealloc
[cachedImage release];
Also, note that the UIImage class reference says:
In low-memory situations, image data
may be purged from a UIImage object to
free up memory on the system. This
purging behavior affects only the
image data stored internally by the
UIImage object and not the object
itself. When you attempt to draw an
image whose data has been purged, the
image object automatically reloads the
data from its original file. This
extra load step, however, may incur a
small performance penalty.
UIImage caches the data itself. You must not hold a pointer and just pass that around. That can be unsafe since when there is a memory warning and there was no strong ref to that object then UIImage will purge cached data. Call [UIImage imageNamed:] every time. It is fast and returns the ref to the image from memory. If the image is no longer in memory it will reload it and pass that ref