I am capturing frames from the camera using the code:
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
:(AVCaptureConnection *)connection
{
// Create a UIImage from the sample buffer data
UIImage *image = [self imageFromSampleBuffer:sampleBuffer];
if(delegate && [delegate respondsToSelector:#selector(captureManagerCapturedFrame:withFrameImage:withFrameBuffer:)]) {
[delegate captureManagerCapturedFrame:self withFrameImage:image withFrameBuffer:sampleBuffer];
}
}
I am doing this because in the delegate method captureManagerCapturedFrame:withFrameImage:withFrameBuffer: I have a flag which tells the app to use either the returned uiimage OR the returned sampleBuffer.
The delegate method is:
- (void) captureManagerCapturedFrame:(AVCamCaptureManager *)captureManager
withFrameImage:(UIImage *)image
withFrameBuffer:(CMSampleBufferRef)frameBuffer {
if(_screen1) {
NSLog(#"Only display camera image\n");
}
else if(_screen2) {
//Enable IR
NSLog(#"Display AND Process camera image\n");
[self imageReconigitionProcessFrame:frameBuffer];
}
}
where imageReconigitionProcessFrame: is:
-(void)imageReconigitionProcessFrame:(CMSampleBufferRef)frameBuffer {
//CFRetain(frameBuffer);
MSImage *qry = [[MSImage alloc] initWithBuffer:frameBuffer orientation:AVCaptureVideoOrientationPortrait]; //MEMORY LEAK HERE???
qry = nil;
//CFRelease(frameBuffer);
}
This code effectively works. But here is my problem. When this code is run and profiled in instruments, I see a rapid increase in the overall bytes used, but the allocations profiler doesn't appear to increase. Nor do a see any 'leaks' using the leaks tool. But clearly, there is a rapid memory gain each time imageReconigitionProcessFrame: is called and the app crashes after a few seconds. When I set frameBuffer to nil, there is NO increase in memory (or course I also don't have the frame buffer to do any processing with).
I have tried transfering ownership of frameBuffer using CFRetain and CFRelease (commented out in the above code), but these don't seem to do anything either.
Does anyone have any idea where I could be leaking memory inside this function???
The method [[MSImage alloc] initWithBuffer: is form a third party SDK (Moodstocks, which is an awesome image recognition SDK) and it works just fine in their demos, so I don't think the problem is inside this function.
First of all, thanks for mentioning Moodstocks (I work for them): we're happy that you find our SDK useful!
To answer your question, I guess your code does indeed contain a leak: at the end of the imageReconigitionProcessFrame method, you should call [qry release]. The rule in Obj-C is quite simple: whenever you manually call alloc on an object, it should also be manually released!
That's BTW what is done in the Moodstocks SDK wrapper: if you look at the [MSScannerSession session: didOutputSampleBuffer:] method, you'll see that we do manually release the MSImage object after it's been processed.
As to why the profiler doesn't find this leak, I guess that it's due to the fact that leaks are analyzed every 10 seconds by default: in this case, the memory leak is so heavy (1280x720 frames, at 15+ FPS if you're on an iPhone 5, for 10 seconds: at least 130 MB leaked) that the code must crash before the first 10 seconds are reached.
Hope this helps!
Related
I am writing a tiny iPhone app to retrieve metadata, such as EXIF info, for all photos stored in the iPhone, and ran into a weird issue when calling the Assets Library Framework API. Basically, if I am calling ALAssetReprsentation's metadata method (http://developer.apple.com/library/ios/documentation/AssetsLibrary/Reference/ALAssetRepresentation_Class/Reference/Reference.html#//apple_ref/occ/instm/ALAssetRepresentation/metadata) for several hundred times (even for the same ALAssetReprsentation object), the API will report an error and return null instead of photo's metadata.
Here is the code to reproduce this issue:
ALAsset *photo = ... // fetch a photo asset via Assets Library Framework
int i = 0;
ALAssetRepresentation *representation = [photo defaultRepresentation];
NSDictionary *metadata;
while (i<600) {
i++;
metadata = [representation metadata];
NSLog(#"photo %d indexed %#", i, metadata);
}
Here is the output for the code above. In the beginning of the output, everything is okay, but after 500+ times, the metadata API will report error like "ImageIO: CGImageSourceCreateWithData data parameter is nil".
...
2011-12-29 21:46:17.106 MyApp[685:707] photo 578 indexed {
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
...
}
...
ImageIO: <ERROR> CGImageSourceCreateWithData data parameter is nil
2011-12-29 21:46:17.151 MyApp[685:707] photo 579 indexed (null)
ImageIO: <ERROR> CGImageSourceCreateWithData data parameter is nil
2011-12-29 21:46:17.177 MyApp[685:707] photo 580 indexed (null)
I am testing in an iPhone 3GS with iOS 5.0.1. And I am developing with Xcode 4.2 with ARC (automatical reference counting) enabled. And I can only reproduce this issue when deploying the app to the iPhone 3GS device, but cannot reproduce this issue when using iOS simulator with the same code (at least I don't reproduce this issue after calling the API over 1800 times in iOS simulator).
Any help is appreciated. Thanks.
It is possible that you are running out of memory. The method [representation metadata] returns an autoreleased object and possibly creates more autoreleased objects when it executes. All these instances are added to the autorelease pool, waiting to be finally released (and their memory freed) when the ARP gets the chance to drain itself.
The problem is that this won't happen until your code returns control to the run loop. So for the duration of your loop, at least 600 large dictionaries (and possibly many more objects) end up being allocated and not deallocated. Depending on the size of these objects, memory usage can increase tremendously.
This is true whether you are using ARC or not.
To avoid this issue, try creating a fresh autorelease pool on every iteration of the loop. That way, the ARP gets drained on every iteration:
while (i<600) {
#autoreleasepool {
i++;
metadata = [representation metadata];
NSLog(#"photo %d indexed %#", i, metadata);
}
}
This is not necessarily the best solution from a performance perspective but at least it will tell you whether the problem is memory related.
PS: Your code doesn't make much sense at the moment. Why retrieve the metadata for the same asset 600 times in a row?
enter code hereMake sure you retain the ALAssetLibrary until you are finished access related assets. From Apple's docs:
The lifetimes of objects you get back from a library instance are tied
to the lifetime of the library instance.
FIXED: I'm not sure yet why. Code has been updated below, and notes at the bottom
I'm having a very strange error that does not show up on the Simulator (even when running Instruments), unless I turn on all the Zombie and Debug options. However, it will crash the phone after a minute of updates (1 update per second). I have a 2D array that I take a subset of, apply a colormap, and turn into an image (this array changes constantly). Then I pass that image to the model, and the viewcontroller grabs it from the model once it receives notification of an update. I'll layout the 3 classes -Spectrogram, Model, ViewController:
Here are the important bits of each (there is more, but not relevant):
Spectrogram.h (Sorry, I can't get this to indent correctly on here)
#interface: Spectrogram : NSObject
{
NSMutableData *arrayData;
}
//renamed so Xcode allows the object with +1 reference count to be returned
- (CGImageRef)newSpectrogramImage;
Spectrogram.m
#implementation Spectrogram
- (CGImageRef)newSpectrogramImage
{
//slightly reordered
NSMutableData *imageData = [[NSMutableData alloc] init];
...code to go through arrayData and colormap it (get RGB transform) and store in imageData...
CGImageRef arrayImage = nil;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
//specifically tell CGImage there is no alpha channel
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNone;
//Use the toll-free bridge between NSData and CFData
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)imageData);
arrayImage = CGImageCreate((slices-startSlice), bins, 8, 24, 3*(slices-startSlice), colorSpace, bitmapInfo, provider, NULL, false, kCGRenderingIntentDefault); //image is rotated now, so width and height are switched
CGColorSpaceRelease(colorSpace);
CGDataProviderRelease(provider);
CGImageRelease(arrayImage);
[imageData release];
//Pass the CGImageRef with a reference count of 1
return arrayImage;
}
Model.h
#interface: Model : NSManagedObject
{
Spectrogram *spectrogram;
}
//Function the viewController can call to get the update
- (CGImageRef)newSpectrogramImage;
Model.m
#implementation Model
... there is a function that adds new data to the array and notifies all listeners...
- (CGImageRef)newSpectrogramImage {
return [spectrogram newSpectrogramImage];
}
ViewController.h
#interface: ViewController : UIViewController
{
}
//the root controller actually alloc's the record, and sets this property when creating this view
#property (nonatomic, retain) Record *currentRecord;
ViewController.m
#implementation ViewController
#synthesize spectrogramView;
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter ... listen for model updates...
}
//now, the real meat
- (void)recordUpdated:(NSNotification*)notification
{
CALayer *myLayer = self.view.layer;
CGImageRef spectrogramImage = nil;
spectrogramImage = [currentRecord newSpectrogramImage];
myLayer.contents = (id)spectrogramImage;
CGImageRelease(spectrogramImage);
}
I've changed this so many times in the last day trying to hunt down where and why it fails. I've tried passing the CGImageRef instead (but since that isn't an object, I'm worried about making copies of what can be a -huge- image) and it still fails. And it works perfectly in the simulator (will run for dozens of minutes). But fails within a minute on the iphone, or if I turn on the debug options for the simulator (it will fail as soon as the viewcontroller is loaded).
On a side note, that might be of some use. This viewcontroller is loaded as a modal view when the phone is turned sideways (works great). However, I have a lot of NSLogs in there and I see this viewcontroller's dealloc is called before the mainviewcontroller even gets to viewwilldisappear - but it still runs. And then this controller's dealloc is called again when the phone is turned back and the view disappears.
Note
The version that worked well in the simulator passed CGImageRefs all the way to the viewController, instead of UIImages. I've tried at least 50 different combinations of where to create the UIImage from the CGImage, and what's posted above is just one of them (all of them fail eventually, or immediately). Of note, with the code above, if I do add this to the viewController modelUpdated:
CGSize size = currentModel.spectrogramImage.size;
NSLog(#"width: %f", size.width);
and comment out assigning it to the spectrogramView, the width is reported correctly, so the UIImage is getting passed along, it's just not getting retained (This is how I understand the EXE_BAD_ACCESS error).
Also, recently I receive dan EXE_BAD_ACCESS on the
self.spectrogramImage = [spectrogram getSpectrogramImage];
line. So, I think the error may be inside the Spectrogram class. Even though the CGImage and UIImage code was taken from Apple examples.
Fixed notes
I read that setting the contents of the CALayer was a much quicker way to pass a CGImageRef to a view - and no UIImage intermediary. Unfortunately, I only commit working changes, so I can't see every iteration I went through. However, I know that I had something very similar to this several times that kept crashing. The problem was always that as it is currently written, the program would crash with EXE_BAD_ACCESS. And if I upped the reference count (or didn't release it), then it would work perfectly, but the object would leak. I still don't understand how that is possible. To have the difference between a leak, and a BAD ACCESS be a reference count of 1 (and not 2 or more).
I can't answer your specific question, but this might help you know what is going on a little bit better. I have a function "logMemUsage" that outputs your memory usage and shows how much it changed since last time. If you call it once a second or so, you can better understand how memory is being used in your app. If it keeps growing, obviously there's a leak, if it goes up and down as you expect it, that's good, if it doesn't go down when you think it should, you'll see it. It's in github here in Utilities.h/.m
It's hard to figure this out from what you've posted, but why not use properties with retain on all your allocated data? For example, you're returning an autoreleased UIImage from getSpectrogramImage, and it gets stored into Model with the call self.spectrogramImage = [spectrogram getSpectrogramImage];, but there isn't a property for spectrogramImage anywhere I can see, even though you're using the self. mechanism. Is it just that you didn't bother to post it? The way it's written it could be getting autoreleased, and then when you try to use it...
I'm quite new to iPhone development. My target is a remote control app, the server of which is TightVNC. But I met a problem and it's driving me crazy... I've successfully connected to the server(using socket), and next would like to request desktop update at least every one second. So here comes the timer which is created through the selector in "performSelectorInBackground". The timer's main task is as fellow:
int picLength;
[self readExact:(char*)(&picLength) bySize:sizeof(int)];
char *picBuffer;
picBuffer = (char *)malloc(picLength);
[self readExact:picBuffer bySize:picLength];
NSData *picData = [[NSData alloc]initWithBytes:picBuffer length:picLength];
[self performSelectorOnMainThread:#selector(setPicInMainThread:) withObject:picData waitUntilDone:YES];
[picData release];
free(picBuffer);
And "setPicInMainThread" is as follow (each picture is around 200KB, iTouch is connected to PC through computer-to-computer wifi, so the speed would be fast enough):
- (void) setPicInMainThread:(NSData *)data {
[chatController.imageView.image release];
chatController.imageView.image = [UIImage imageWithData:data];
}
The app crushes after presenting the first desktop update. I am wondering if I've met the "memory leak" concerning NSTimer and NSData, which lots of people is talking about... If so, is there any way to solve the problem? Thank you very much for helping!
Your malloc code is looking bad. Why would you take the address of an int and then cast it as a character pointer? Then why would even pass an int for the size to the same function you pass a char * to get set? I have a feeling you are getting an EXC_BAD_ACCESS and it is because of your first few lines and the readExact method. Make sure you get the length the correct and pass the correct parameters.
int picLength;
[self readExact:(char*)(&picLength) bySize:sizeof(int)];
char *picBuffer;
picBuffer = (char *)malloc(picLength);
I have been working with Apple's iPhone CoreDateRecipes sample code to learn more about tableviews and core data. I have coded my own test app based off of that sample, and it works well except for one thing. When I choose a photo for the 'recipe', no matter if it is from the camera or the library, when I hit "Done" to leave editing mode, it takes about 15 seconds before returning control to the user. This happens when testing on the device - in simulator, there is still a delay, but it is only 2-4 seconds.
I tested the "edit/done" button without choosing a photo and editing other data, and it saves instantaneously, so I'm pretty sure the image is to blame. Below is the code where it leaves editing mode, and the image processing code - what can I add/change/remove to speed this up? I know these sample code pieces are just proofs of concept, but I can't believe they published an example with such a crappy user experience!
Thanks, as always, for any guidance...let me know if there is any other code you need to see, or you can see the whole sample project here
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
[super setEditing:editing animated:animated];
[self updatePhotoButton];
nameTextField.enabled = editing;
overviewTextField.enabled = editing;
[self.navigationItem setHidesBackButton:editing animated:YES];
if (!editing) {
NSManagedObjectContext *context = recipe.managedObjectContext;
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Error in RecipeDetailViewController:setEditing -- %#, %#",error, [error userInfo]);
abort();
}
}
}
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)selectedImage editingInfo:(NSDictionary *)editingInfo {
NSManagedObject *oldImage = recipe.image;
if (oldImage != nil) {
[recipe.managedObjectContext deleteObject:oldImage];
}
NSManagedObject *image = [NSEntityDescription insertNewObjectForEntityForName:#"Image" inManagedObjectContext:recipe.managedObjectContext];
recipe.image = image;
[image setValue:selectedImage forKey:#"image"];
CGSize size = selectedImage.size;
CGFloat ratio = 0;
if (size.width > size.height) {
ratio = 70.0 / size.width;
} else {
ratio = 70.0 / size.height;
}
CGRect rect = CGRectMake(0.0, 0.0, ratio * size.width, ratio * size.height);
UIGraphicsBeginImageContext(rect.size);
[selectedImage drawInRect:rect];
recipe.thumbnailImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self dismissModalViewControllerAnimated:YES];
}
First, as Gilbert pointed out, example code is not meant for production use and will be slow.
Second, you should not store images in Core Data. The example may show how to do it but it is generally a very bad idea. You should be storing images on disk and then keeping a pointer (file path) to the image in Core Data. There are some exceptions to this rule (very small images) but you should rethink your design.
Lastly, A lot of the slowness you are seeing may not be Core Data related. The image picker code is very slow on its own. I would recommend changing the code to just write to disk and see how slow that is compared to the writing to Core Data. I would be surprised if it was much faster.
Update
You can store small images inside of Core Data and my advice from other posts stands, mores for the desktop than iOS. The reason for this is the cache. On iOS, the cache that Core Data uses is very small. If you store images in the database that are large, you can blow out that cache easily and cause future calls that should be in the cache to hit the disk instead. This is not a hard and fast rule of "don't store binary data" but more of a rule of watch your performance and if you are hitting the disk more than you should, binary data could easily be the cause.
Contacts
With regard to contacts, you can be slower because they are doing things differently than you are and they could easily be using private APIs to access the camera. Because it is Apple, they don't necessarily play by the same rules as us.
I'm not an iPhone developer, but generally, example code does not take into account the user experience. Example code shows examples.
In general, you need to perform expensive (long running) operations in additional threads.
Maybe this blog post will help: Respect the Main Thread
I downloaded the project, built it and ran it in the simulator. I couldn't reproduce your problem. I found that the time it took to save an image was visually instantaneous i.e. no longer than the view transition.
If you see the same issue on the original, unmodified project, then you have something else going on. Make sure you have the latest version of the project which should be the one at your link. I know there is at least one really old one floating around because I hit it recently.
i release an image with [myimageview.image release];
but after the app was in background it comes foreground again it release that image again, so it crash! All my tries to check if the app came from a background did not worked.
So how could i check if an object is already released.. so i dont do it twice?
thankx
chris
EDIT
After several complains about my bad coding :) here an example:
First I initialize an Array with the path to a lot of fullscreen images
Also there are around 10 Different Arrays for 10 different Scenes.
When I put directly the Images in an array it just needed to much memory from the beginning, or when i released a scene totaly , it needed to load the whole image array again and that came to slow. So I just load the path to the images into an array and assign each pic in a loop to my imageview while runtime.
Init once:
imageArray_stand= [[NSArray alloc] initWithObjects:
#"FrankieArmeRaus_0001.jpg",
... up to 60 Images
#"FrankieArmeRaus_0061.jpg",nil];
In a Loop thats called each 1/10 Second:
if ([myimageview.image retainCount] > 1)
//if ( myimageview.image != nil) // does not work = crash
{
[myimageview.image release];
myimageview.image = nil;
}
myimageview.image = [UIImage imageNamed:[imageArray_stand2 objectAtIndex:piccounter-1]];
Problem came, because when the app went into background and than into foreground again it seems to release the image 2 times, so i needed a solution to check that.
I am happy about any solution (just NOW it works) thats better.
Even to load all images completly into an array, but as mentioned it needs to much mem and to reassign while runtime needs to long to load. (1 sec for 50 Images)
Also I needed to RELASE and set to NIL, because otherwise it would even make my memory usage out of limit.
You never, ever, release a property of another object. You release the entire myimageview object, or you just assign to it's properties. How those properties are memory manged is the private business of the myimageview object.
Easiest way is to do something like this:
if (someObject != nil)
{
[someObject release];
someObject = nil;
}
However, I don't think you should be releasing myimageview.image, assuming that myimageview is a UIImageView. The UIImageView is responsible for managing its image; you shouldn't be messing with its retain count.
Maybe what you really want to do is this:
myimageview.image = nil;
This will cause myimageview to release it and stop pointing to the now-invalid memory.
solved it with
if ([myimageview.image retainCount] > 1) [myimageview.image release];