Memory leaks in ipod - 100% leak while using instruments and iPod - xcode

-(NSString*)readTextColumnWithIndex:(int)index {
char* val = (char *)sqlite3_column_text(statement,index);
if (val==NULL) {
return nil;
}
return [NSString stringWithUTF8String:val];
}
I have 100% leak at return [NSString stringWithUTF8String:val];. Not sure how to fix them. I believe only alloc, retain etc could end up in memory leaks. Any help is greatly appreciated.

[NSString stringWithUTF8String:val] is the statement that created the object that is leaking - you need to see what happens to it next.
Are you retaining the NSString returned by readTextColumnWithIndex: somewhere?

-(NSDictionary*)loadDataRow:(SqlQuery*)q {
return [NSDictionary dictionaryWithObjectsAndKeys: [NSString stringWithFormat:#"%d",[q readIntColumn]],
#"TOCID",[q readTextColumn],#"SubChapter_Name",[q readTextColumn],
#"HtmlFileName",[q readTextColumn],#"ImageName",[q readTextColumn],
#"RefsTitleSection",[NSString stringWithFormat:#"%d",
[q readIntColumn]],#"PageId",
nil];
}
This is where it is called. readIntColumn in turn calls readTextColumnWithIndex and NSDictionary is returned back to an the caller method
chap_Arr = nil;
while([query step]) {
ChapName = [query readTextColumn];
chap_Arr = [mData objectForKey:ChapName];
if (!chap_Arr) {
chap_Arr = [[[NSMutableArray alloc] init] autorelease];
[mData setObject:chap_Arr forKey:ChapName];
}
[chap_Arr addObject:[self loadDataRow:query]];
}
I am not sure how i can release the chap_Arr as it is in a loop. I do have an RELEASE_SAFELY in dealloc.

Related

macOS 10.13: "Scheduling the NSURLDownload loader is no longer supported."

Running my macOS app in macOS 10.13, I see printed to the console:
Scheduling the NSURLDownload loader is no longer supported.
What does this mean?
The Sparkle Updater seems to be the culprit in the instances I have found. I guess the Sparkle dev team will be on to it and hopefully we'll no longer see the message after Sparkle is updated.
It appears to mean You have just created an instance of the deprecated class NSURLDownload.
To show this, create a new Cocoa command-line tool project in Xcode and replace the code in main.m with the following:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
#autoreleasepool {
NSURL* url = [[NSURL alloc] initWithString:#"https://example.com"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:30.0];
NSLog(#"Will print strange sentence to console") ;
[[NSURLDownload alloc] initWithRequest:request
delegate:nil];
NSLog(#"Did print strange sentence to console") ;
}
return 0;
}
Build and run. I get the following result in console (timestamps removed):
Will print strange sentence to console:
Scheduling the NSURLDownload loader is no longer supported.
Did print strange sentence to console
I would say the "fix" is to replace the deprecated NSURLDownload with NSURLSession.
You can correct it directly in the source code for Sparkle. Update SUAppcast.m file at line 82 by replace the NSURLDownload with the following:
NSURLSessionDownloadTask *downloadTask = [[NSURLSession sharedSession] downloadTaskWithRequest:request completionHandler:^(NSURL *location, __unused NSURLResponse *response, NSError *error) {
if (location) {
NSString *destinationFilename = NSTemporaryDirectory();
if (destinationFilename) {
// The file will not persist if not moved, Sparkle will remove it later.
destinationFilename = [destinationFilename stringByAppendingPathComponent:#"Appcast.xml"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *anError = nil;
NSString *fromPath = [location path];
if ([fileManager fileExistsAtPath:destinationFilename])
[fileManager removeItemAtPath:destinationFilename error:&anError];
BOOL fileCopied = [fileManager moveItemAtPath:fromPath toPath:destinationFilename error:&anError];
if (fileCopied == NO) {
[self reportError:anError];
} else {
self.downloadFilename = destinationFilename;
dispatch_async(dispatch_get_main_queue(), ^{
[self downloadDidFinish:[[NSURLDownload alloc] init]];
});
}
}
} else {
[self reportError:error];
}
}];
[downloadTask resume];

NSPasteboard readObjectsForClasses:options: returns value twice

Right to the point, then:
First snippet (AppDelegate):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
//...code taken out...
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent *incomingEvent) {
if ([incomingEvent type] == NSKeyDown) {
NSUInteger flags = [incomingEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
if (flags==NSCommandKeyMask && ([incomingEvent keyCode] == 8)) {
[ClipboardUtilities logger:#"cmd+c recognized"];
[self determineAndAddToHistory];
}
}
}];
}
Second snippet (AppDelegate):
-(void) determineAndAddToHistory {
id clipDat = [ClipboardUtilities getClipboardDataNatively];
if ([clipDat isKindOfClass:[NSAttributedString class]])
NSLog(#"clipDat.string = %#",((NSAttributedString*)clipDat).string);
}
Third snippet (ClipboardUtilities class):
+(id) getClipboardDataNatively {
NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
NSArray *classArray = #[[NSAttributedString class], [NSImage class]];
NSDictionary *options = [NSDictionary dictionary];
NSArray *objectsToPaste = nil;
BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options];
if (ok) {
objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options];
}
NSLog(#"objectsToPaste count = %li",[objectsToPaste count]);
return [objectsToPaste objectAtIndex:0];
}
I've noticed some strange behaviour that I will try to explain with an example:
Input
Cmd+C the string "A"
Cmd+C the string "B"
Cmd+C the string "C"
Cmd+C the string "D"
Output from determineAndAddToHistory
A
A
B
C
So I'm noticing that it retains that first item for some reason...then returns me the second most recent item each time. I've tried outputting the objectsToPaste array in the getClipboardDataNatively method, and this still is the case. Could someone please let me know how I would approach this problem, or how they've solved it?
P.S. my ClipboardUtilities class does not implement any Delegates, or inherit from anything but NSObject.
Well I guess since nobody likes long questions (I'll have to figure out how to shorten this), I figured something out. For some reason, I get the hotkey call really quickly (the clipboard gets updated AFTER the key is called in fact). As a result, I just have a small delay, and my model is now updated properly:
NSTimer* briefDelay = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(determineAndAddToHistory) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:briefDelay forMode:NSRunLoopCommonModes];
I do not invalidate the timer manually, as per the documentation:
repeats
If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.

NSView Drag Operation Error

I'm coding in Cocoa, on OS X.
I'm trying to receive a file that is dragged and dropped onto my NSView subclass - which I can do; and get its contents and filename and display them - which I can do for any type of file first time, but on the second time, when I try and drag another file on, I can only set the title setTitle:, but not the main body text setText:
The errors I am getting are:
Canceling drag because exception 'NSInternalInconsistencyException' (reason 'Invalid parameter not satisfying: aString != nil') was raised during a dragging session
and
Assertion failure in -[NSTextFieldCell _objectValue:forString:errorDescription:], /SourceCache/AppKit/AppKit-1187/AppKit.subproj/NSCell.m:1532
My code (sorry, it's quite long!):
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
NSPasteboard *pboard;
NSDragOperation sourceDragMask;
sourceDragMask = [sender draggingSourceOperationMask];
pboard = [sender draggingPasteboard];
if ([[pboard types] containsObject:NSFilenamesPboardType]) {
NSURL *file = [NSURL URLFromPasteboard:pboard];
//NSData *data = [NSData dataWithContentsOfURL:file];
NSError *error;
NSStringEncoding encoding;
NSString *contentString = [[NSString alloc] initWithContentsOfURL:file usedEncoding:&encoding error:&error];
NSLog(#"Error: %#",error);
NSString *last = [[file path] lastPathComponent];
NSArray *parts = [last componentsSeparatedByString:#"."];
NSString *filename = [parts objectAtIndex:0];
NSString *fileType = [parts objectAtIndex:1];
NSLog(#"FILETYPE: %#", fileType);
if ([fileType isEqualToString:#"txt"] || [fileType isEqualToString:#"md"]) {
[self setTitle:filename];
if (self.textViewString == (id)[NSNull null] || self.textViewString.length == 0) {
[self setText:contentString];
} else {
BOOL whatToDo = (NSRunCriticalAlertPanel(#"What do you want to do?", nil, #"Append", #"Replace", nil) == NSAlertDefaultReturn);
if (whatToDo) {
//Append
[self setText:[NSString stringWithFormat:#"%#\n%#",self.textViewString,contentString]];
} else {
//Replace
[self setText:contentString];
}
}
return YES;
} else {
return NO;
}
} else if ([[pboard types] containsObject:NSPasteboardTypeString]) {
NSString *draggedString = [pboard stringForType:NSPasteboardTypeString];
if (self.textViewString == (id)[NSNull null] || self.textViewString.length == 0) {
[self setText:draggedString];
} else {
[self setText:[NSString stringWithFormat:#"%#\n%#",self.textViewString,draggedString]];
}
return YES;
}
else {
return NO;
}
}
Thanks in advance! :)
Sounds like Cocoa is canceling the drag when any exception is raised, and an exception is getting raised when something internally is expecting a string and is getting a nil value instead.
It's just a guess without having more information, but I would predict that stringWithFormat: is raising the exception, as it looks like the only really potentially fragile bit of what you have written.
You are doing a couple ill-advised things. First, you are assuming that -initWithContentsOfURL:usedEncoding:error: is succeeding. You should not do this. Instead, you need to pass an NSError ** that can be filled in on error, test whether contentString is nil, and, if so, check the error accordingly. I have a feeling that you will find you are getting nil, and the error will explain why.
Possibly unrelated, but your if (whatToDo) is not doing what you probably think it is. Since whatToDo is a pointer to an autoreleased NSNumber instance your conditional will always evaluate to true, since the pointer is non-zero. What you likely meant to do was something like the following:
BOOL whatToDo = (NSRunCriticalAlertPanel(#"What do you want to do?", nil, #"Append", #"Replace", nil) == NSAlertDefaultReturn);
if (whatToDo) {
//Append
[self setText:[NSString stringWithFormat:#"%#\n%#",self.textViewString,contentString]];
} else {
//Replace
[self setText:contentString];
}
Lots of thanks for the many tips and pieces of advice from this answer by Conrad Shultz! I've followed the advice and tips there.
However, my problem turned out to be very, very basic. It lied in the line BOOL whatToDo = (NSRunCriticalAlertPanel(#"What do you want to do?", nil, #"Append", #"Replace", nil) == NSAlertDefaultReturn);
It turns out a string must be passed to the second parameter, but I was passing nil. Fixed!

Getting crash after picking images from UIImagePickerController (Related to memory leak?)

I have been trying to minimize my memory footprint with UIImagePickerController, but I'm starting to think that the memory problems I am having are resulting from poor memory management, instead of a particular way to handle the UIImagePickerController object.
My workflow is this: The "Edit Image" button is clicked, which presents a UIActionSheet. This action sheet allows you to delete, take a picture, choose from the library, or cancel. If you select Choose from the library or Take Picture, I alloc an instance of UIImagePickerController and present it, followed by a release of UIImagePickerController:
-(void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (actionSheet.tag != 999) {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
BOOL pickImage = nil;
if (actionSheet.tag == iPhoneWithDelete) {
switch (buttonIndex) {
case 0:
object.objectImage = nil;
pickImage = NO;
break;
case 1:
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
pickImage = YES;
break;
case 2:
imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
pickImage = YES;
break;
default:
pickImage = NO;
break;
}
} else if (actionSheet.tag == iPhoneNoDelete) {
switch (buttonIndex) {
case 0:
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
pickImage = YES;
break;
case 1:
imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
pickImage = YES;
break;
default:
pickImage = NO;
break;
}
} else if (actionSheet.tag == iPodWithDelete) {
switch (buttonIndex) {
case 0:
object.objectImage = nil;
pickImage = NO;
break;
case 1:
imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
pickImage = YES;
break;
default:
pickImage = NO;
break;
}
} else if (actionSheet.tag == iPodNoDelete) {
switch (buttonIndex) {
case 0:
imagePicker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
pickImage = YES;
break;
default:
pickImage = NO;
break;
}
}
if (pickImage) {
imagePicker.allowsEditing = YES;
[self presentModalViewController:imagePicker animated:YES];
} else {
[self setupImageButton];
[self setupChooseImageButton];
}
[imagePicker release];
}
}
Once I get a selection back from the UIImagePickerController, I save 2 images, a resized version of the edited image to use for a thumbnail, and a 800x600 version of the original unedited image into a relationship attribute (Transformational, using the same UIImage to PNG transformations found in the Recipes demo code) for display use: (the resize methods are based on the one demoed in this SO post.)
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[self dismissModalViewControllerAnimated:YES];
NSManagedObject *oldImage = object.imageFull;
if (oldImage != nil)
{
[object.managedObjectContext deleteObject:oldImage];
}
NSManagedObject *image = [NSEntityDescription insertNewObjectForEntityForName:#"Image" inManagedObjectContext:object.managedObjectContext];
object.imageFull = image;
UIImage *rawImage = [info objectForKey:#"UIImagePickerControllerOriginalImage"];
CGSize size = CGSizeMake(800, 600);
UIImage *fullImage = [UIImageManipulator scaleImage:rawImage toSize:size];
[image setValue:fullImage forKey:#"imageFull"];
UIImage *processedImage = [UIImageManipulator scaleImage:[info objectForKey:#"UIImagePickerControllerEditedImage"] toSize:CGSizeMake(75, 75)];
object.objectImage = processedImage;
[self setupImageButton];
[self setupChooseImageButton];
rawImage = nil;
fullImage = nil;
processedImage = nil;
}
When I go through viewDidUnload I am setting self.object = nil, and [object release] during dealloc, but I'm still getting memory warnings after about 10 image changes, with a crash at around 20. It leads me to believe that I am not getting that full image out of memory the correct way. What am I missing here?
And on a second note, does the Camera source use significantly more memory than the Photo Albums source? I tend to get more crashes when using the camera.
--EDIT--
Starting a bounty for any information about what I may be handling wrong. I will update this post with any answers to anything I have been unclear about. Just at my wit's end on this.
--EDIT 2--
Reworked the code to take chrissr's suggestions into account, and implemented GCD to improve usability. Is this as efficient as this process gets? Still getting memory warnings, and a crash around 20 images in. I'm sure that the combination of doing expensive UIImage resizing and using UIImagePickerController is murdering the CPU, but I can't imagine that every app is dealing with uncertainty around the UIImagePickerController. My memory footprint is around 2 megs. I have been operating under the assumption that that was plenty of overhead. Should I reduce that footprint further?
Here is the modified code:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
[self dismissModalViewControllerAnimated:YES];
if (object.imagePath != nil) {
[self deleteImages];
}
dispatch_queue_t image_queue;
image_queue = dispatch_queue_create("com.gordonfontenot.app", NULL);
dispatch_async(image_queue, ^{
NSDate *now = [NSDate date];
NSDateFormatter *f = [[NSDateFormatter alloc] init];
[f setDateFormat:#"yyyyMMDDHHmmss"];
NSString *imageName = [NSString stringWithFormat:#"Image-%#-%i", [f stringFromDate:now], arc4random() % 100];
NSString *thumbName = [NSString stringWithFormat:#"%#-thumb", imageName];
[f release];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:imageName];
NSString *thumbPath = [documentsDirectory stringByAppendingPathComponent:thumbName];
NSData *thumbImageData = UIImagePNGRepresentation([UIImageManipulator scaleImage:[info objectForKey:#"UIImagePickerControllerEditedImage"] toSize:CGSizeMake(120, 120)]);
[thumbImageData writeToFile:thumbPath atomically:NO];
dispatch_async(dispatch_get_main_queue(), ^{
object.thumbPath = thumbPath;
[self setupImageButton];
imageButton.enabled = NO;
[self setupChooseImageButton];
});
NSData *fullImageData = UIImagePNGRepresentation([UIImageManipulator scaleImage:[info objectForKey:#"UIImagePickerControllerOriginalImage"] toSize:CGSizeMake(800, 600)]);
[fullImageData writeToFile:fullPath atomically:NO];
dispatch_async(dispatch_get_main_queue(), ^{
imageButton.enabled = YES;
object.imagePath = fullPath;
});
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera) {
UIImageWriteToSavedPhotosAlbum([info objectForKey:#"UIImagePickerControllerOriginalImage"], self, nil, nil);
}
});
dispatch_release(image_queue);
}
Memory warnings are extremely common when dealing with the UIImagePickerController. This is especially true when using the camera. Keep in mind that while a JPG or PNG on disk may only amount to a few MB, the uncompressed in memory bitmap used to draw the image uses considerably more.
There's nothing that you're doing wrong necessarily, but some improvements can be made:
Rather than storing the image bytes in Core Data, why not write the image to disk and store the path to the file in your database?
Rather than using so many autoreleased images, can you find a way to manage their lifecycle directly and release them sooner?
Your best bet may be to write the images to disk as soon after processing as possible and free up the memory they're using. Then store their location using Core Data rather than the raw data.

Memory problems with NSMutableDictionary, causing NSCFDictionary memory leaks

Help me please with the following problem:
- (NSDictionary *)getGamesList
{
NSMutableDictionary *gamesDictionary = [[NSMutableDictionary dictionary] retain];
// I was trying to change this on the commented code below, but did have no effect
// NSMutableDictionary *gamesDictionary = [[NSMutableDictionary alloc] init];
// [gamesDictionary retain];
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSArray *gameDate = [key componentsSeparatedByString:#" "];
NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
NSString *date_time = [NSString stringWithFormat:#"%#, %#",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
[[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
[_id release];
}
sqlite3_reset(statement);
return gamesDictionary;
}
The leak starts in another method of another class, there the getGamesList method is called, like this:
NSMutableDictionary *gamesDictionary;
gamesDictionary = [[NSMutableDictionary dictionaryWithDictionary:[appDelegate getGamesList]] retain];
After that there are a lot of leaks that points to NSCFArray in the string:
NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain];
in this method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSArray *keys = [[NSArray arrayWithArray:[gamesDictionary allKeys]] retain];
if ([keys count] != 0) return [[keys objectAtIndex:section] uppercaseString];
return #"";
}
I assume these things are connected to each other, but I still can not understand all of the memory management tips.
Thanks a lot!
Didn't use Cocoa for years (that's why I can't tell you an exact answer :/). But I guess your problem is that you systematically use retain on your objects.
Since the object reference count never get to 0, all dictionaries are keep in memory and not freed.
Try to remove the retain on [NSArray arrayWithArray] and [NSMutableDictionary dictionaryWithDictionary
http://en.wikibooks.org/wiki/Programming_Mac_OS_X_with_Cocoa_for_beginners/Some_Cocoa_essential_principles#Retain_and_Release
It does look like you are over-retaining your array.
When you create the gamesDictionary it is created with an retain count of +1. You then retain it (count becomes +2). When you get the value outside this function you retain again (count becomes +3).
You are correct that if you create an object you are responsible for it's memory management. Also, when you get an object from a method, you should retain it if you want to keep it around for longer than the span of the function. In your case, you just want to get at some of the properties of the object, so you don't need to retain it.
Here is a suggestion:
- (NSDictionary *)getGamesList
{
NSMutableDictionary *gamesDictionary = [NSMutableDictionary dictionary]; // Remove the retain.
while (sqlite3_step(statement) == SQLITE_ROW)
{
NSString *key = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];
NSArray *gameDate = [key componentsSeparatedByString:#" "];
NSNumber *_id = [[NSNumber alloc] initWithInt:sqlite3_column_int(statement, 0)];
NSString *date_time = [NSString stringWithFormat:#"%#, %#",[gameDate objectAtIndex:0],[gameDate objectAtIndex:2]];
if (![gamesDictionary valueForKey:date_time]) [gamesDictionary setValue:[NSMutableArray array] forKey:date_time];
[[gamesDictionary valueForKey:date_time] addObject:[[_id copy] autorelease]];
[_id release];
}
sqlite3_reset(statement);
return gamesDictionary;
}
This next bit is messy. you create a new dictionary and retain it. The original dictionary is not autoreleased, so the count isn't decremented and it always hangs around. Just assign the dictionary rather than create a new one.
NSMutableDictionary *gamesDictionary = [[appDelegate getGamesList] retain];
// Retaining it, becuase it looks like it's used elsewhere.
Now, in this method:
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
NSString *returnString;
// Don't need to retain the keys because you are only using it within the function
// and since you didn't alloc, copy or retain the array it contains, you aren't responsible for it's memory management.
NSArray *keys = [NSArray arrayWithArray:[gamesDictionary allKeys]];
if ([keys count] != 0) {
returnString = [[NSString alloc] initWithString:[[keys objectAtIndex:section] uppercaseString]];
return [returnString autorelease];
}
return #"";
}

Resources