NSImage and related APIs leaking memory - macos

Following is the code snippet that I have:
// Make Auto release pool
NSAutoreleasePool * autoReleasePool = [[NSAutoreleasePool alloc] init];
try
{
if (mCapture)
{
// Get the image reference
NSImage* image = NULL;
image = [mCapture getCurrentFrameImage];
// Get the TIFF data
NSData *pDataTifData = [[NSData alloc] initWithData:[image TIFFRepresentation]];
NSBitmapImageRep *pBitmapImageRep = [[NSBitmapImageRep alloc] initWithData:pDataTifData];
// Convert to BMP data
NSData *pDataBMPData;
pDataBMPData = [pBitmapImageRep representationUsingType: NSPNGFileType
properties: nil];
// Save to specified path
ASL::String strPath = ASL::MakeString(capInfo->thefile.name);
NSString* pPath = (NSString*)ASL::MakeCFString(strPath);
[pDataBMPData writeToFile:pPath
atomically: YES];
::CFRelease(pPath);
pDataBMPData = nil;
[pBitmapImageRep release];
pBitmapImageRep = nil;
[pDataTifData release];
pDataTifData = nil;
image = nil;
}
}
catch(...)
{
}
[autoReleasePool drain];
Note that image = [mCapture getCurrentFrameImage]; is returning an autoreleased NSImage. I am releasing objects and also have NSAutoreleasePool in place. But still it is leaking about 3-4 MB of memory everytime this code snippet is executed. I am not sure where the mistake is.

You could simplify this code a lot by making captureCurrentFrameImage return an NSBitmapImageRep instead of an NSImage, since you never actually use an NSImage for anything here. You can wrap the image rep in an image when necessary, and for this code, simply use the image rep by itself to produce the PNG data. Among other things, this saves you a trip through the TIFF representation.
If it still leaks after you make those changes, run your app under Instruments's Leaks template; the two instruments in that template, Leaks and ObjectAlloc, will help you hunt down whatever leaks you have.

Related

How to handle multiple images with large size?

I have list of selected images from Photos gallery.
The array list contain selected PHAssets from Photos
<PHAsset: 0x131334350> 768AD8BA-FE7A-4EE9-B2CF-4DD4AEBE2AE9/L0/001 mediaType=1/0, sourceType=1, (2448x3264), creationDate=2016-03-11 06:50:09 +0000, location=1, hidden=0, favorite=0 ",
"<PHAsset: 0x131334260> CA962E42-A367-4E8D-85C8-934F8C76FE78/L0/001 mediaType=1/0, sourceType=1, (2448x3264), creationDate=2016-03-11 06:50:08 +0000, location=1, hidden=0, favorite=0 "
Now i want to store this images into Document Directory.For that i am converting PHAssets to original image and store it in array, for that code i did
#property (nonatomic, strong) PHImageRequestOptions *requestOptions;
for (PHAsset *asset in assetArray) {
self.requestOptions = [[PHImageRequestOptions alloc] init];
self.requestOptions.resizeMode = PHImageRequestOptionsResizeModeExact;
self.requestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
// this one is key
self.requestOptions.synchronous = true;
PHImageManager *manager = [PHImageManager defaultManager];
__block UIImage *ima;
// Do something with the asset
[manager requestImageForAsset:asset
targetSize:PHImageManagerMaximumSize
contentMode:PHImageContentModeDefault
options:self.requestOptions
resultHandler:^void(UIImage *image, NSDictionary *info) {
ima = image;
[Albumimages addObject:ima];
}];
}
But problem is that when i selected multiple images with large size(more than 25) app is crash down. Because when i convert PHAsset to original image by using PHImageManager it uses high memory of device.
Please help me to solve this problem. Provide me if there is any other solution to handle selected PHAssets for storing images in application document directory.
PHImageRequestOptions *option = [PHImageRequestOptions new];
option.synchronous = NO;
let screenSize: CGSize = UIScreen.mainScreen().bounds.size
let targetSize = CGSizeMake(screenSize.width, screenSize.height)
[manager requestImageForAsset:asset
targetSize:target
contentMode:PHImageContentModeAspectFill
options:option resultHandler:^(UIImage *result, NSDictionary *info)
{
//this block will be called synchronously
}];
If you set maximum image size when using more images they could crash but if set target size then handles it.
Handle all images with the data instead of covert into original image.
[manager requestImageDataForAsset:asset
options:self.requestOptions
resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info)
{
// UIImage *image = [UIImage imageWithData:imageData];
[Albumimages addObject:imageData];
}];
After that Write image data at document directory path.

Is it possible to re-size a image in a uipasteboard xcode 7.2 Objective C?

I’m currently using this code bellow in Xcode 7.2, which takes the image and pastes it in imessages etc. But it’s too large (in dimensions). Is it possible to make this image smaller?
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSData *imgData = UIImagePNGRepresentation(image);
[pasteboard setData:imgData forPasteboardType:[UIPasteboardTypeListImage objectAtIndex:0]];
Not whilst it's in the pasteboard. BUT, if you're worried about it being the wrong size when you paste it somewhere, FEAR NOT, Apple have been kind to us and have built quite a lot of the presentation "resizing" into things for us.
What you need to do if you really HAVE to resize the image is:
1) Be lazy and use code which you can resume... i.e. I found this on here I believe:
- (NSImage *)imageResize:(NSImage*)anImage newSize:(NSSize)newSize {
NSImage *sourceImage = anImage;
[sourceImage setScalesWhenResized:YES];
// Report an error if the source isn't a valid image
if (![sourceImage isValid]){
NSLog(#"Invalid Image");
} else {
NSImage *smallImage = [[NSImage alloc] initWithSize: newSize];
[smallImage lockFocus];
[sourceImage setSize: newSize];
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
[sourceImage drawAtPoint:NSZeroPoint fromRect:CGRectMake(0, 0, newSize.width, newSize.height) operation:NSCompositeCopy fraction:1.0];
[smallImage unlockFocus];
return smallImage;
}
return nil;
}
then in the bit of code which you've given us:
*pasteboard = [UIPasteboard generalPasteboard];
NSImage *myShinyResizedImage = [self imageResize: oldImage newSize: CGSizeMake(100.0, 100.0)];
NSData *imgData = UIImagePNGRepresentation(myShinyResizedImage);
[pasteboard setData:imgData forPasteboardType:[UIPasteboardTypeListImage objectAtIndex:0]];
And it's been resized before if goes off to get pasted elsewhere.

How to draw EPS data on NSView

I'm struggling with the problem to draw an eps file on a NSView.
When I first load the eps file from a file and draw it with drawInRect: the image is displayed correctly. However, the image will not be drawn when I load it from an archive file.
I've prepared a dirty small example that you can copy/paste and try out. Create a new Cocoa App project and add this to the delegate method.
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Just a sample eps file
NSURL *url = [NSURL URLWithString: #"http://embedded.eecs.berkeley.edu/concurrency/latex/figure.eps"];
NSImage *epsImage = [[[NSImage alloc] initWithContentsOfURL: url] autorelease];
// Encode data
NSMutableData *mutableData = [[[NSMutableData alloc] init] autorelease];
NSKeyedArchiver *coder = [[[NSKeyedArchiver alloc] initForWritingWithMutableData: mutableData] autorelease];
[coder encodeObject: epsImage forKey: #"image"];
[coder finishEncoding];
NSString *dataFile = [#"~/Desktop/image.data" stringByExpandingTildeInPath];
[mutableData writeToFile: dataFile atomically: YES];
// Decode data
NSData *data = [NSData dataWithContentsOfFile: dataFile];
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData: data];
NSImage *loadedImage = [decoder decodeObjectForKey: #"image"];
// Draw image
NSRect rect;
rect.origin = NSZeroPoint;
rect.size = loadedImage.size;
NSView *view = [[NSApp mainWindow] contentView];
[view lockFocus];
[loadedImage drawInRect: rect fromRect: rect operation: NSCompositeSourceOver fraction: 1.0];
[view unlockFocus];
}
To prove that the first loaded image draws correctly just change the line [loadedImage drawInRect:...] to [epsImage drawInRect:...].
I'm using NSKeyedArchiver and NSKeyedUnarchiver here for simulating encodeWithCoder: and initWithCoder:. So please focus on the fact that NSImage with NSEPSImageRep representation, which does not contain a preview (from a resource fork?) and loaded purely as eps commands, is not drawn on a NSView correctly.
Any help is appreciated.
Due to the way that cacheing works on NSImage, I've often found it more effective to actually grab the NSImageRep if I know what the type is.
In our code, we found that the most reliable way to save off images is in their original format, but that requires either saving off the data in its original format somewhere else, or requesting the data from the NSImageRep. Unfortunately, there's not a generic -(NSData*)data method of NSImageRep, so we ended up specifically checking for various types of NSImageRep and saving them off depending on what we knew them to be.
Fortunately, loading is simple, as NSImage::initWithData: will figure out the type based on the data.
Here's our long-standing code for doing this. Basically, it prefers PDF then EPS then it makes a TIFF of anything it doesn't understand.
+ (NSData*) dataWithImage:(NSImage*)image kindString:( NSString**)kindStringPtr
{
if (!image)
return nil;
NSData *pdfData=nil, *newData=nil, *epsData=nil, *imageData=nil;;
NSString *kindString=nil;
NSArray *reps = [image representations];
for (NSImageRep *rep in reps) {
if ([rep isKindOfClass: [NSPDFImageRep class]]) {
// we have a PDF, so save that
pdfData = [(NSPDFImageRep*)rep PDFRepresentation];
PDFDocument *doc = [[PDFDocument alloc] initWithData:pdfData];
newData = [doc dataRepresentation];
if (newData && ([newData length]<[pdfData length])) {
pdfData = newData;
}
break;
}
if ([rep isKindOfClass: [NSEPSImageRep class]]) {
epsData = [(NSEPSImageRep*)rep EPSRepresentation];
break;
}
}
if (pdfData) {
imageData=pdfData;
kindString= #"pdfImage";
} else if (epsData) {
imageData=epsData;
kindString=#"epsImage";
} else {
// make a big copy
NSBitmapImageRep *rep0 = [reps objectAtIndex:0];
if ([rep0 isKindOfClass: [NSBitmapImageRep class]]) {
[image setSize: NSMakeSize( [rep0 pixelsWide], [rep0 pixelsHigh])];
}
imageData = [image TIFFRepresentation];
kindString=#"tiffImage";
}
if (kindStringPtr)
*kindStringPtr=kindString;
return imageData;
}
Once we have the NSData*, it can be saved in a keyed archive, written to disk or whatever.
On the way back in, load in the NSData* and then
NSImage *image = [[NSImage alloc] initWithData: savedData];
and you should be all set.

UIView layer renderInContext Memory not getting released

We are trying to create multiple pdf files by using UIView.layer's renderInContext method
The below code run in a autoreleasepool.
---loop
UIGraphicsBeginPDFContextToFile(documentDirectoryFilename, CGRectZero, nil);
UIGraphicsBeginPDFPage();
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
[view.layer renderInContext:pdfContext];
view =nil;
pdfContext =nil
UIGraphicsEndPDFContext()
--loop ends
After couple of iterations resident memory increases to 20 mb and subsequent iteration adds to the total memory used.
Some how ARC is not releasing the memory used in rendering the pdf and thus the application crashes with low memory.
Application is using ARC
Any help or pointers to resolve the issue would be much appreciated.
Thanks
UPDATED:
Thanks for the quick reply.
My apologies for having a typo in the pseudocode.
Here is sample code from a test project.
-(BOOL)addUIViewToPDFFile:(UIView*)view newBounds:(CGRect)newBounds pdfFile:(NSString*)pdfFile{
BOOL retFlag =YES;
NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);
NSString* documentDirectory = [documentDirectories objectAtIndex:0];
NSString* documentDirectoryFilename = [documentDirectory stringByAppendingPathComponent:pdfFile];
UIGraphicsBeginPDFContextToFile(documentDirectoryFilename, CGRectZero, nil);
CGContextRef pdfContext = UIGraphicsGetCurrentContext();
if([NSThread isMainThread]){
NSLog(#"Main Thread");
}else{
NSLog(#"Not in Main Thread");
}
UIGraphicsBeginPDFPage();
logMemUsage1();
[view.layer renderInContext:pdfContext];
UIGraphicsEndPDFContext();
pdfContext = nil;
logMemUsage1();
return retFlag;
}
- (IBAction)createPdf:(id)sender {
for(int i=0;i<10;i++){
#autoreleasepool {
PDFViewController *vc = [[PDFViewController alloc] init];
[vc loadView];
NSString *PdfFileName =[ NSString stringWithFormat:#"TestPdf%d.pdf",i ];
[self addUIViewToPDFFile:vc.view newBounds:self.view.frame pdfFile:PdfFileName];
vc = nil;
}
}
}
One more to point to add is that when application goes into the background on press of the hardware home button and comes back again to foreground the memory seems to get cleared and released.
I am expecting that after each iteration the memory which is used for creating the pdf should be released.

Convert PDF pages to images with COCOA

I am having problem with PDF conversion to images. I would like to create an image file for every page in PDF document.
This is the code I am using and works fine. Every page gets converted into the image, but I have problem with image resolution. I don't know how to set the resolution of the output images. Can someone help me out?
NSData *pdfData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://localhost/test/test.pdf"]];
NSPDFImageRep *img = [NSPDFImageRep imageRepWithData:pdfData];
NSFileManager *fileManager = [NSFileManager defaultManager];
int count = [img pageCount];
for(int i = 0 ; i < count ; i++) {
[img setCurrentPage:i];
NSImage *temp = [[NSImage alloc] init];
[temp addRepresentation:img];
NSBitmapImageRep *rep = [NSBitmapImageRep imageRepWithData:[temp TIFFRepresentation]];
NSData *finalData = [rep representationUsingType:NSJPEGFileType properties:nil];
NSString *pageName = [NSString stringWithFormat:#"Page_%d.jpg", [img currentPage]];
[fileManager createFileAtPath:[NSString stringWithFormat:#"%#/%#", #"/Users/mac/Desktop/", pageName] contents:finalData attributes:nil];
}
Thanks a lot!
Since NSPDFImageRep is a subclass of NSImageRep, couldn't you use the [NSImageRep drawInRect:] method?
Link: http://developer.apple.com/mac/library/documentation/cocoa/reference/ApplicationKit/Classes/NSImageRep_Class/Reference/Reference.html#//apple_ref/doc/uid/20000346-drawInRect_
The simplest way would be to use the ImageIO framework. Feed the PDF data to an image source to get a CGImage; feed that object to an image destination to generate (and optionally save in the same step) the JPEG data. In the latter step, you can specify the resolution among the image properties; see “Individual Image Properties” in the documentation.
Don't forget to finalize your destination. It's vital.

Resources