handle NSError using ARC - leak - iphone

- (BOOL)parserJSONString:(NSString *)jsonString error:(NSError **)anError {
//some data getting
//error handle
NSString *description = #"phone number couldn't be using";
NSString *recoverySuggestion = #"Please provide an other phone number.";
NSInteger errorCode = -1;
NSArray *keys = [NSArray arrayWithObjects: NSLocalizedDescriptionKey, NSLocalizedRecoverySuggestionErrorKey, nil];
NSArray *values = [NSArray arrayWithObjects:description, recoverySuggestion, nil];
NSDictionary *userDict = [NSDictionary dictionaryWithObjects:values forKeys:keys];
*anError = [[NSError alloc] initWithDomain:#"my domain" code:errorCode userInfo:userDict];
return NO;
}
*anError = [[NSError alloc] initWithDomain:#"my domain" code:errorCode userInfo:userDict]; compiler give next leak warning
"Potential null dereference. According to coding standards in 'Creating and Returning NSError Objects' the parameter '' may be null"
How to fix this?

You need to first check whether anError is nil or NULL:
if (anError) {
*anError = [[NSError alloc] initWithDomain:#"my domain" code:errorCode userInfo:userDict];
}

This is not actually a leak warning, but a potential dereference of a null pointer. The compiler is complaining about the line
*anError = [[NSError alloc] initWithDomain:#"my domain" code:errorCode userInfo:userDict];
You assign to the location being pointed to by anError without checking, whether anError is actually the null pointer (which is allowed "according to the coding standard", and may happen, if the caller is not interested in detailed error information).

Related

NSArray losing values when I put self into it

So I have this piece of code:
- (void) connectSelector:(NSArray *)args {
NSError* error;
NSString* data = [NSString stringWithContentsOfURL:[NSURL URLWithString:[args objectAtIndex:0]] encoding:NSASCIIStringEncoding error:&error];
NSLog(#"%#", data);
NSDictionary* dictionary = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:#"connector", #"data", #"error", nil] forKeys:[NSArray arrayWithObjects:self, data, error, nil]];
[[args objectAtIndex:1] performSelector:#selector(dataDownloaderDidDownloadData:) withObject:dictionary];
}
The values NSArray in dictionary is losing its 'self' value. Why is this so?
Thanks in advance!
Looks like you've got the keys and values element back-to-front. You probably meant:
NSDictionary* dictionary = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:self, data, error, nil] forKeys:[NSArray arrayWithObjects:#"connector", #"data", #"error", nil] ];
Or to use modern syntax:
NSDictionary *dictionary = #{ #"connector" : self, #"data" : data, #"error" : error };
(Not every object can be used as a key in a dictionary).
Might be the case with arrayWithObjects(but not sure),because it returns an auto-released array.Use initWithObjects which returns an array you must then release to avoid a memory leak.
Check This : iPad large NSArray - initWithObjects vs. ArrayWithObjects

Memory management creating an NSDictionary with NSMutableArrays

I'm having problem understanding memory management when creating a dictionary with mutable arrays. I'm using the ios6 SDK with deployment target 5.1.
In the implementation of the class "Group" the method "namesAndEmails" builds an array "emails" that contains the emails addresses for Person objects with an email. If the Person object does not have an email the Person name is added to another array "namesWithNoEmail". The arrays are returned in a dictionary.
#import "Group.h"
#implementation Group
-(NSDictionary*) namesAndEmails {
NSMutableArray *emails = [[NSMutableArray alloc] initWithCapacity:0] ;
NSMutableArray *namesWithNoEmail = [[NSMutableArray alloc] initWithCapacity:0];
NSString *email;
NSString *name;
for (Person *p in allPersons) {
email = p.email;
name = p.name;
if ([email length]==0) {
[namesWithNoEmail addObject:name];
} else {
[emails addObject:email];
}
}
NSArray *keys = [NSArray arrayWithObjects:#"emails",#"names", nil];
NSArray *objects = [NSArray arrayWithObjects:emails, namesWithNoEmail, nil];
//[emails release];
//[namesWithNoEmail release];
return [NSDictionary dictionaryWithObjects:objects forKeys:keys];
}
Somewhere else in the code I wish to send an email to a group of people so I call the emailGroup method which gets a dictionary out by calling "namesAndEmails" on the group.
-(void) emailGroup:(Group*) g {
NSDictionary *emailInfo = [g namesAndEmails];
guestsWithNoEmail = [emailInfo objectForKey:#"names"];
guestEmails = [emailInfo objectForKey:#"emails"];
int nGuestsWithNoEmail = [guestsWithNoEmail count];
if (nGuestsWithNoEmail > 0) {
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"No emails" message:#"" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] autorelease];
[alert show];
}
// some more code here
MFMailComposeViewController *picker = [[MFMailComposeViewController alloc] init];
picker.mailComposeDelegate = self;
[picker setSubject:subject];
[picker setMessageBody:#"" isHTML:NO];
[picker setToRecipients:guestEmails];
[[self delegate ] presentModalViewController:picker animated:YES];
[picker release];
}
As far as I understand [NSDictionary dictionaryWithObjects:objects forKeys:keys] in "namesAndEmails" returns an autoreleased dictionary. But why does my code crash if I release the "emails" and "namesWithNoEmail" arrays? I thought that the dictionary would have ownership of the array after they are added and therefore it would be safe to release the arrays in the method. I guess that's not correct, but why?
Is the a more clean way of doing this? Thank you for any advice!
My first suggestion would be to use the "Product->Analyze" feature. If you leaking or over releasing somewhere, it will probably give you the exact chain of events.
Secondly, I can't see the linking between your methods nameAndEmails and emailGroup:. Because I can't see the connection, I can't tell you if the autorelease is causing the problem.
Autoreleased objects get released when the the main run loop cycles. So it's very possible your NSDictionary is getting released. You could test this by doing anything from setting the memory location as a "watch" in the debugger to putting printing something in the console lines each time the runloop your in cycles (I made the assumption your in the main run loop, so correct me if that's not true).
Other things you can do to track the problem would be to use "Zombies" in instruments or NSZombieEnable=YES in your configuration

Declared and synthesized NSArray works in all methods but one

I have an array of NSMutableDictionaries which has been sorted.
This array has been declared and synthesized so that its reachable anywhere in the code. However, its not.
When I try to read it out in cellForRowAtIndexPath, I get 0x5852400 does not appear to point to a valid object in debugger by using the po command, and I get the EXC_BAD_ACCESS error when using NSLog.
Code:
- (void)request:(FBRequest *)request didLoad:(NSArray*)result
{
int counter;
friendsArray = [[NSMutableArray alloc] init];
for (NSDictionary *d in [result objectForKey:#"data"])
{
[friendsArray addObject:d];
counter++;
}
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"first_name" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
newfriendsArray = [[NSArray alloc] init];
newfriendsArray = [friendsArray sortedArrayUsingDescriptors:sortDescriptors];
NSLog(#"The new array which works and has been sorted: %#", newfriendsArray);
[[self tableView] reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
NSLog(#"Array still working here: %#", newfriendsArray);
return [newfriendsArray count];
}
Doing the same NSlog like the ones above in cellForRowAtIndexPath will cause the simulator to crash.
It's a memory management error. In this line:
newfriendsArray = [friendsArray sortedArrayUsingDescriptors:sortDescriptors];
sortedArrayUsingDescriptors: returns an autoreleased object that you need to retain if you want to use it for longer than the current method. The line above:
newfriendsArray = [[NSArray alloc] init];
has no effect other than introduce a memory leak to your code. It seems you have not quite understood when you need to create new objects and when other methods do this for you. In addition, you should really use properties to set your ivars (self.newFriendsArray = ...;) to avoid these simple memory management errors.

How to dissect and reorganize info in an NSDictionary

So I have an array of NSDictionaries, each NSDictionary has a bunch of key/value pairs pertaining to aspects of a photo (from Flickr).
I'm making an app that has a UITableViewController whose cells should be each of the different categories of the photos. So in pseudocode, I'm trying to construct a new NSDictionary (with keys being categories of photos, values being the NSDictionaries of the photos that contains that key). I'm iterating through each NSDictionary in the initial array, getting the category tags, and saying, if my new NSDict doesn't contain this key, make a new key to an empty array. Then add the current NSDict to that array. I'm getting consistent errors, not sure why.
Here's the diluted code.
photoList = [FlickrFetcher photosWithTags:[NSArray arrayWithObjects: #"CS193p_SPoT", nil]];
NSLog(#"%#", photoList);
categories = [[NSDictionary alloc] init];
NSArray *temp = [[NSArray alloc] init];
for (id obj in photoList) {
temp = [[obj objectForKey:#"tags"] componentsSeparatedByString:#" "];
for (id string in temp) {
if (![categories objectForKey:string]) {
NSMutableArray *arr = [[NSMutableArray alloc] init];
[categories setObject:arr forKey:string];
//[arr release];
}
NSMutableArray *photos = [categories objectForKey:string];
[photos addObject:obj];
[categories setObject:photos forKey:string];
}
}
Thanks!
NSDictionary doesn't have a method setObject:forKey:. You need an NSMutableDictionary.
self.categories = [NSMutableDictionary dictionary];
Other than that, please do use Joost's excellent rewrite of your code.
SIGABRT, just so you know, most likely means that an assertion somewhere failed. In this case, it may be an assertion all the way down in CoreFoundation*; CF checks for mutability when you try to access a dictionary like that and causes an interrupt if the object isn't mutable.
*I have just learned about the CF source's availability recently and have been looking through it, so this may be just "new thing" bias and incorrect.
I don't notice any errors (syntax-errors, that is) in your code, however here is an updated piece of code which has been implemented a bit cleaner (and without memory leaks)
self.photoList = [FlickrFetcher photosWithTags:[NSArray arrayWithObjects: #"CS193p_SPoT", nil]];
NSLog(#"%#", photoList);
self.categories = [NSDictionary dictionary];
for (NSDictionary *obj in photoList) {
NSArray *temp = [[obj objectForKey:#"tags"] componentsSeparatedByString:#" "];
for (NSString *string in temp) {
NSMutableArray *photos = [categories objectForKey:string];
if (!photos) {
photos = [NSMutableArray array];
[categories setObject:photos forKey:string];
}
[photos addObject:obj];
}
}
If it's not working please tell us the exact warning, and were it is caused.

Wordpress XML-RPC call from Objective C: wp.newComment

I'm using Eric Czarny's Cocoa XML-RPC framework to make a call to the Wordpress API's. I've downloaded the sample app from Wordpress which gives some good examples. Unfortunately the good examples are for every call EXCEPT wp.newComment.
I'm trying to post a comment using the code below and I keep getting an error with a localized description that tells me to check my input parameters. I've checked and rechecked and I don't understand what is wrong.
Any ideas?
NSDictionary *commentStructure = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:0], #"comment_parent", #"xmlrpc anonymous comments plugin now enabled", #"content", #"Test Author", #"author", #"http://iphone.someurl.com", #"author_url", #"someemailaddy#hotmail.com", #"author_email", nil];
NSArray *args = [NSArray arrayWithObjects:[NSNumber numberWithInt:0], #"", #"", [NSNumber numberWithInt:[self.parentFeedItem.postID intValue]], commentStructure, nil]; // the param(s)
NSString *server = [[[NSString alloc] initWithString:#"http://www.someurl.com/xmlrpc.php"] autorelease]; // the server
NSString *method = [[[NSString alloc] initWithString:#"wp.newComment"] autorelease]; // the method
XMLRPCRequest *request = [[XMLRPCRequest alloc] initWithHost:[NSURL URLWithString:server]];
[request setMethod:method withObjects:args];
id response = [self executeXMLRPCRequest:request];
[request release];
if( [response isKindOfClass:[NSError class]] ) {
//return nil;
NSLog(#"There was a problem");
NSLog([response localizedDescription]);
}
I found that I had downloaded an older version of the example Wordpress source code which did not have an example for wp.newComment.
You can find a much newer version of the source code here which does have better examples.
http://iphone.trac.wordpress.org/browser
Using the examples from the newer code I was able to resolve my issues.