Problem filling NSMutableArray w/ UIImage objects - objective-c

I am having a baffling issue while trying to fill an NSMutableArray with UIImages.
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderMask;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
while(...) // <--- Iterates through data sets
{
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, numBytes, NULL);
CGImageRef cImage = CGImageCreate(iw, ih, 8, 32, 4 * iw, colorSpace, bitmapInfo, provider, NULL, NO, renderingIntent);
UIImage *finalImage = [UIImage imageWithCGImage:cImage];
[images addObject:finalImage]; // <--- Declared and instantiated earlier
[delegate sendImage:finalImage];
CGImageRelease(cImage);
CGDataProviderRelease(provider);
}
CGColorSpaceRelease(colorSpace);
[delegate operationCompletedWithImages:images];
That is how I have the code running. So I basically have a function running in the while statement that returns the next set of bitmap data, I then create a UIImage and store it into the mutable array.
I've tested this by writing each file to disk and then accessing them which results in the proper set of images. The problem is when using the array to keep data in memory, accessing any image object in the array I get the same exact image over and over. The image is also always the last image of the set.
I've tried testing the contents by setting the array as a UIImageView animation array and by using an NSTimer to cycle the contents. Either way it is just the same image (last image pulled) over and over.
I have this operation running inside a subclassed NSOperation object so it doesn't block the interface. Another interesting aspect here is that when the images array sent with operationCompletedWithImages was giving me the array of duplicate images I tried using the sendImage message and store the images in a different array inside the delegate object (thinking maybe it was a threading issue). This gave me the same results.
I've been stuck on this for over a week with no progress. I've never seen anything like it and I can't find anyone else who has had a similar issue.I would be happy to provide extra information if someone feels it would assist in solving this issue.
If anyone could provide any assistance I would greatly appreciate it.

Not sure if this is the problem, but you should probably be notifying your delegate of the changes on the main thread, not the thread that the NSOperation is running on.
[delegate performSelectorOnMainThread:#selector(sendImage:) withObject:finalImage waitUntilDone:YES];
And likewise for the last message. If the delegate is modifying anything UIKit-related, it must be invoked on the main thread!

One might assume that the NSMutableArray would handle the memory management of the object being added to it, but perhaps because you're dealing with the C level Quartz helpers/ Core wrappers this may not be the case.
In other image manipulating methods I've experimented with they are usually wrapped in an autorelease pool if they are involved with threads.
Have you tried experimenting with release/autorelease on the finalImage?

Figured out the problem. Turned out to be an issue with the backing data pointer.
Where before I was accessing data everytime thinking it would iterate and overwrite with new contents, and the new contents would be plugged into my CGDataProviderRef. This was not the case, I basically ended up supplying a pointer to the same backing data for the Provider. The solution was to copy the data into an NSData object and use that copy for the DataProvider.
So instead of:
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, numBytes, NULL);
It would be:
NSData *pxData = [[NSData alloc] initWithBytes:pFrameRGB->data length:numBytes];
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)pxData);

Related

Issues with threading, blocks, CGImageRef and scope

I have a program where I set up a completion block that is running on a background thread. Inside the block I set up a CGImageRef and then on the main thread set my layer contents. THe problem is, sometimes the app crashes on the main thread portion.
This is the completion block, in the code below both the fullImage and cfFullImage are declared in my .h
requestCompleteBlock completionBlock = ^(id data)
{
// Seems I need to hold onto the data for use later
fullImage = (NSImage*)data;
NSRect fullSizeRect = NSMakeRect(0, 0, self.frame.size.width, self.frame.size.height);
// Calling setContents with an NSImage is expensive because the image has to
// be pushed to the GPU first. So, pre-emptively push it to the GPU by getting
// a CGImage instead.
cgFullImage = [fullImage CGImageForProposedRect:&fullSizeRect context:nil hints:NULL];
// Rendering needs to happen on the main thread or else crashes will occur
[self performSelectorOnMainThread:#selector(displayFullSize) withObject:nil waitUntilDone:NO];
};
The final line of my completion block is to call displayFullSize. That function is below.
- (void)displayFullSize
{
[self setContents:(__bridge id)(cgFullImage)];
}
Do you see or know of any reason why the setContents might fail?
Thanks
Joe
cgFullImage is not retained. The CGImage Core Foundation object is deallocated and you are using a deallocated object.
Core Foundation object pointer types like CGImageRef are not managed by ARC. You should either annotate the instance variable with __attribute__((NSObject)), or change the type of the instance variable to an Objective-C object pointer type like id.

How do i set a CGContextRef property rather than using UIGraphicsGetCurrentContext()

Ive been trying to work with UIGraphicsGetCurrentContext and CGContextRef. I was told that using UIGraphicsGetCurrentContext() numerous times is bad and to rather work on using CGContextRef and referring to it.
I have been trying to work on the second part and I am having issues setting the #property and referring to it. Can someone give me a declaration and usage example of this? Tried googling and couldn't find any references to it.
Ta
You probably shouldn't store the return value from UIGraphicsGetCurrentContext in a property. You usually either don't know how long the context will be valid, or the context has a short lifetime anyway. For example, if you're calling UIGraphicsGetCurrentContext from your drawRect: method, you don't know how long that context will survive after you return from drawRect:. If you're calling UIGraphicsGetCurrentContext after calling UIGraphicsBeginImageContextWithOptions, you will probably be calling UIGraphicsEndImageContext soon anyway. It would be inappropriate to keep references to those contexts around.
If you are calling many Core Graphics functions on the same context, then you want to store the context in a local variable. For example, here is a drawRect: method from one of my test projects:
- (void)drawRect:(CGRect)dirtyRect {
NSLog(#"drawRect:%#", NSStringFromCGRect(dirtyRect));
[self layoutColumnsIfNeeded];
CGContextRef gc = UIGraphicsGetCurrentContext();
CGContextSaveGState(gc); {
// Flip the Y-axis of the context because that's what CoreText assumes.
CGContextTranslateCTM(gc, 0, self.bounds.size.height);
CGContextScaleCTM(gc, 1, -1);
for (NSUInteger i = 0, l = CFArrayGetCount(_columnFrames); i < l; ++i) {
CTFrameRef frame = CFArrayGetValueAtIndex(_columnFrames, i);
CGPathRef path = CTFrameGetPath(frame);
CGRect frameRect = CGPathGetBoundingBox(path);
if (!CGRectIntersectsRect(frameRect, dirtyRect))
continue;
CTFrameDraw(frame, gc);
}
} CGContextRestoreGState(gc);
}
You can see that I'm doing a bunch of stuff with the context: I'm saving and restoring the graphics state, I'm changing the CTM, and I'm drawing some Core Text frames into it. Instead of calling UIGraphicsGetCurrentContext many times, I call it just once and save the result in a local variable named gc.

Objects not initialized

in objective C, I can do the following
UIImage *myImage = [UIImage imageNamed:#"myPhoto.jpg"];
variable.image = myImage;
and this works just fine. but the object named "myImage" was never initialized, and the UIImage never had any memory allocated and yet the code still works..
Can someone explain what's going on here?
Yes, the object was initialised. The imageNamed: method allocates and initialises an object, sends it an autorelease message, then returns the memory address to you. You store that memory address in the pointer called myImage.
myImage and the object are two different things. myImage merely points at a memory location. It is not the object itself.
You can pass around objects without assigning them to variables, and you can assign one object to many variables.
Consider this:
UIImage *imageOne;
UIImage *imageTwo;
imageOne = [UIImage imageNamed:#"myPhoto.jpg"];
imageTwo = imageOne;
The image wasn't copied. There is only one object in existence. Both variables point to it.
Now consider this:
NSLog(#"%#", [UIImage imageNamed:#"myPhoto.jpg"]);
You didn't assign it to any variable. But the object still existed, right?
Have a look in the documentation for UIImage. Under the heading "Cached Image Loading Routines" and "Creating New Images" are some methods with a + where you might expect a -. This means that calling these methods is the same as calling [[[UIImage alloc] init] autorelease];, though some of them do more (as in custom initialization).
Lots of other Objective C objects have similar methods, for example, NSArray has a method +(id)array that creates and returns an empty array. Your main indicator here is the + instead of the -, but I always check the documentation to check how the initialization is handled and to make sure the object is autoreleased.

objc leak behavior I can't explain

The following code loop does not leak memory (as verified by watching it loop infinitely under "top");
NSBitmapImageRep *this_bmap = 0;
while (1) {
CGImageRef windowImage =
CGWindowListCreateImage(CGRectNull,
kCGWindowListOptionIncludingWindow,
windowID, kCGWindowImageDefault);
this_bmap = [[NSBitmapImageRep alloc] initWithCGImage:windowImage];
[this_bmap release];
CGImageRelease(windowImage);
}
and I would not expect it to. However, when I copy a pointer to the bitmap data, like this:
NSBitmapImageRep *this_bmap = 0;
while (1) {
CGImageRef windowImage =
CGWindowListCreateImage(CGRectNull,
kCGWindowListOptionIncludingWindow,
windowID, kCGWindowImageDefault);
this_bmap = [[NSBitmapImageRep alloc] initWithCGImage:windowImage];
void *pixels1 = [this_bmap bitmapData];
[this_bmap release];
CGImageRelease(windowImage);
}
this now leaks like crazy. I can see this happening rapidly under "top" and the program eventually grinds to a halt.
I am new to Objective-C, but I am not new to programming and I can't understand this behavior. The documentation for the method bitmapData claims it simply returns a pointer (as opposed to allocating something), so I am stumped. I found a similar question from some time ago, but the only answer was to "look into pools" and I don't see how that could help here.
Any ideas what's going on here?
Accessing the pixel data causes the object to be retained and autoreleased so that the bitmap data doesn't suddenly go away unexpectedly. To see your expected results (i.e., loop not consuming memory with each iteration), rewrite as:
NSBitmapImageRep *this_bmap = 0;
while (1) {
NSAutoreleasePool* loopPool = [NSAutoreleasePool new];
CGImageRef windowImage =
CGWindowListCreateImage(CGRectNull,
kCGWindowListOptionIncludingWindow,
windowID, kCGWindowImageDefault);
this_bmap = [[NSBitmapImageRep alloc] initWithCGImage:windowImage];
void *pixels1 = [this_bmap bitmapData];
[this_bmap release];
CGImageRelease(windowImage);
[loopPool drain];
}
hmmm, I'm no guru so I'm just guessing here. But it occurs to me that every time through to loop you are effectively declaring a new variable called pixels1. Therefore each time through you allocate some new space rather than reuse it. Try moving the declaration of pixels1 outside the loop and see what happens.
Note, this is just a guess and may be wrong. A more knowledgable person may be able to give you a definitive answer.
The "initWithCGImage:" call has this in its documentation:
Discussion
If you use this method, you
should treat the resulting bitmap
NSBitmapImageRep object as read only.
Because it only retains the value in
the cgImage parameter, rather than
unpacking the data, accessing the
pixel data requires the creation of a
copy of that data in memory. Changes
to that data are not saved back to the
Core Graphics image.
That seems to indicate that the memory returned from bitmapData needs to be CFRelease'ed.

Huge memory leak in NSBitmapImageRep

I have a method that analyzes pixel data inside an NSBitmapImageRep that is built from a CGImageRef. Here is the relevant code:
CGImageRef ref;
// omitted code for initializing ref
NSBitmapImageRep *bitmapRep = [[NSBitmapImageRep alloc] initWithCGImage:ref];
uint32* bitmapPixels = (uint32*) [bitmapRep bitmapData];
// do stuff with bitmapPixels
[bitmapRep release];
CGImageRelease(ref);
I know I'm properly releasing the CGImageRef and NSBitmapImageRep, but the call to -bitmapData leaks about 2 MB each time it's called, and I don't know how to properly release it. Any ideas?
Update: I forgot to add one important point: memory is only leaked when there is a full screen application running. For regular usage, the memory is released just fine.
Are you doing this in a loop? If so you might need to make an autorelease pool to make sure memory is cleaned up in a timely fashion. See autorelease pools.
The bitmap data should be owned by either the CGImage or the NSBitmapImageRep (or copied to an autoreleased behind-the-scenes NSData object by the latter). As such, whichever object owns (or copies) it should release it.
Are you seeing contrary results in heap/Instruments?