EXEC_BAD_ACCESS error, memory management issues - iphone

I've read that this error comes from memory-management issues such as sending a message to an object which has been released. I am getting the error right after the comment "output information about songs in the 'info' array", in the first line of that second 'for' section. Is the info array not storing the objects that I am giving it in the first 'for' section?
Any other ideas?
MPMediaQuery *query = [[MPMediaQuery alloc] init]; //query iPod library
NSMutableArray *info; //create array to hold songs that fit
NSArray *allSongs = [query collections];
//only add those songs which have not
//been played since last login
for (MPMediaItem *recent in allSongs) {
NSDate *lastPlayed = [recent valueForProperty:MPMediaItemPropertyLastPlayedDate];
BOOL uploadInfo = [[PlayedSinceLastLogin alloc] initWithLastPlayedDateOfSong:lastPlayed] ;
if (uploadInfo == YES) {
[info addObject:recent];
}
}
//output information about songs
//in the 'info' array
for (MPMediaItem *played in info) {
NSString *playCount = [played valueForProperty:MPMediaItemPropertyPlayCount];
NSString *lastPlayed = [played valueForProperty:MPMediaItemPropertyLastPlayedDate];
NSString *songTitle =
[played valueForProperty: MPMediaItemPropertyTitle];
NSString *artistName =
[played valueForProperty: MPMediaItemPropertyArtist];
NSString *albumName =
[played valueForProperty: MPMediaItemPropertyAlbumTitle];
NSLog (#"\n %# by %#, from album %#, played since last login.\nLast Played:%#.\nTotal Play Count: %#.", songTitle, artistName, albumName, lastPlayed,playCount);
}

It doesn't look like you've ever actually created an instance of NSMutableArray for info to point to, so the bad access error is probably due to attempting to treat some random bit of memory as an initialized object.
Change
NSMutableArray *info;
to
NSMutableArray *info = [NSMutableArray array];

Related

working with json data

I have the follow code that parses JSON data received from a server:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSAutoreleasePool *pool=[[NSAutoreleasePool alloc] init];
NSArray *array_webdata=[[NSArray array] init];
NSString *searchStatus = [[NSString alloc] initWithData:webData encoding:NSUTF8StringEncoding];
array_webdata = [parsedata objectWithString:searchStatus error:nil];
NSDictionary *usersList = [array_webdata valueForKey:#"results"];
//I think that is not a real NSDictionary because if I write NSArray *keys = [usersList allKeys]; the execution crashes
NSLog(#"\n usersList =\n %# \n", usersList);
[searchStatus release];
[connection release];
[webData release];
[pool drain];}
the json data stored in usersList has the structure:
(
{
createTime = "date hour";
fullname = "user name";
"prof_id" = number;
thumb = "image.jpg";
},
{
data of rest of users...
}
)
And I would like create a class to store the data of each user and use "prof_id" when I want to use a particular use.
I need this because the app needs a list with all users (not tableview) and I think this is de easiest way.
Can someone help me? Thanks!!
Please used JsonKit Framework to parse json data received from web service.
Read data and parse using JSONKit:
NSData* jsonData = [NSData dataWithData:webData];
JSONDecoder* decoder = [[JSONDecoder alloc]
initWithParseOptions:JKParseOptionNone];
NSArray* json = [decoder objectWithData:jsonData];
After that, you'll have to iterate over the json variable using a for loop.
Create new class with the name User (file->new->file) inherited from NSObject class, create required parameters in .h/.m file.(do synthesize to generate getter/setter for attributes)
import User.h in your connection class and create objects of User entity in iterator loop and add those object in global scope array.
for(NSDictionary *userInfo in json) {
User* user=[[User alloc] init];
user.fullName=[userInfo valueForKey:#"fullname"];
user.prof_id=[[userInfo valueForKey:#"prof_id"] integerValue];
// Add into your global array
[usersList addObject:user];
[user release];// if ARC is not enable
}
// Check for successful object creation
NSLog(#"USER LIST contain User class Objects- %#",userList);
if i'm not wrong the only thing you need to do is :
NSMutableArray *yourArray = usersList;
and then with a for loop like
for(int i = 0;i<[usersList count] ;i++)
{
NSMutableDictionary *yourDictionary = [usersList objectAtIndex:i];
int prof_id = [yourDictionary valueForKey:#"prof_id"];
}
you can get your prof_id like that.
i hope this helps...
Use JSON Framework, and parse data using below code.
NSString* newStr = [[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:#"yout link to json file"] encoding:NSUTF8StringEncoding error:nil];
NSLog(#"new str - %#",newStr);
NSArray *response = [newStr JSONValue];
NSLog(#"json array - %#",response);
Use the response array to show your results.

help on sorting NSMutableDictionary strings that are URL images

i want to populate the tablecell with title and imageurl from xml list.
i manage to store the title (NSMutableDictonary *sections )and imageURL (NSMutableDictonary *sectionsImg) into 2 NSMutableDictionary respectively.
/*******This is in viewDidLoad***/
Directory *allDirectory = [appDelegate.directories objectAtIndex:0];
for (allDirectory in appDelegate.directories)
{
NSDictionary *dica = [NSDictionary dictionaryWithObject:allDirectory.dirTitle forKey:#"dirTitle"];
NSDictionary *dico = [NSDictionary dictionaryWithObject:allDirectory.imageURL forKey:#"imageURL"];
[dirName addObject:dica];
[dirImage addObject:dico];
//NSLog(#"dic of items : %#",dirImage);
}
for (allDirectory in appDelegate.directories)
{
//retrieve the first letter from every directory title (dirTitle)
NSString * c = [allDirectory.dirTitle substringToIndex:3];
NSString * m = allDirectory.imageURL;
found = NO;
find = NO;
for (NSString *str in [self.sections allKeys])
{
if ([str isEqualToString:c])
{
found = YES;
}
}
for (NSString *stra in [self.sectionsImg allKeys])
{
if([stra isEqualToString:m])
{
find = YES;
}
}
if (!found)
{
[self.sections setValue:[[NSMutableArray alloc]init] forKey:c ];
[self.sectionsImg setValue:[[NSMutableArray alloc]init] forKey:m];
}
if (!find)
{
[self.sectionsImg setValue:[[NSMutableArray alloc]init] forKey:m];
}
}
for (NSDictionary *directory in dirName)
{
[[self.sections objectForKey:[[directory objectForKey:#"dirTitle"] substringToIndex:3]] addObject:directory];
//NSLog(#"hehehe have : %#",sections);
}
for (NSDictionary *directoryImg in dirImage)
{
//[[self.sectionsImg objectForKey:[[directoryImg objectForKey:#"imageURL"] substringFromIndex:0]] addObject:directoryImg];
[[self.sectionsImg objectForKey:[directoryImg objectForKey:#"imageURL"]] addObject:directoryImg];
//NSLog(#"HOHOHO have : %#",sectionsImg);
}
And on cellForRowAtIndexPath i declare a dictionary
NSDictionary *dictionary = [[self.sections valueForKey:[[[self.sections allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
cell.textLabel.text = [dictionary objectForKey:#"dirTitle"];
but when i tried to declare a dictionary for imageURL
NSDictionary *dictionaryImg = [[self.sectionsImg valueForKey:[[[self.sectionsImg allKeys] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)] objectAtIndex:indexPath.section]] objectAtIndex:indexPath.row];
it gives me a error :
Terminating app due to uncaught exception 'NSRangeException', reason: '* -[NSMutableArray objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
any idea why? the logic is supposed to be the same where xml title and url can be retrieve and be displayed. Title is retrievable but imageURL is not. Help is deeply appreciated !
You are trying to sort an array... except for the fact your array isn't an array, but a NSDictionary.
Your code isn't the best at the moment. Your getting the idea of Dictionaries wrong and may be confusing them with arrays, so my best guess is your quite new to programming into objective-c.
You have two lists of things, if I'm not mistaken. The first list is the list of names, and the second list is an image corresponding with that name.
Below I'm going to do two things:
Firstly, I'm giving you two ways on how to fix your problem. It has a sample code included and gives you a small explanation with it. The possibility exist you don't understand parts of what I describe. In that case, you should;
Check out the link I described below the two solutions. It has a tutorial which makes you understand everything about arrays, dictionaries, tables and, as a bonus, XML-parsing.
So, in my opinion, you can do two things:
The first one is using an array of NSDictionaries. You'd be using a code which looks like:
NSMutableDictionary *itemOne = [[NSMutableDictionary alloc] init];
NSMutableDictionary *itemTwo = [[NSMutableDictionary alloc] init];
NSMutableArray *listOfAll = [[NSmutableArray alloc] init];
NSString *itemOneName = [[NSString alloc] initWithFormat:#"This is picture 1"];
NSString *itemTwoName = [[NSString alloc] initWithFormat:#"This is picture 2"];
NSData *imageOneData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: #"http://myurl/mypic1.jpg"]];
NSData *imageTwoData = [[NSData alloc] initWithContentsOfURL: [NSURL URLWithString: #"http://myurl/mypic2.jpg"]];
UIImage *itemOneImage = [UIImage imageWithData: imageOneData];
UIImage *itemTwoImage = [UIImage imageWithData: imageTwoData];
[itemOne setObject:itemOneNameString forKey:#"Name"];
[itemOne setObject:itemOneImage forKey:#"Image"];
[itemTwo setObject:itemTwoNameString forKey:#"Name"];
[itemTwo setObject:itemTwoImage forKey:#"Image"];
[listOfAll addObject:itemOne];
[listOfAll addObject:itemTwo];
Anything can be filled using that array. Just use something with a for-loop to iterate through your array.
for (int i = 0; i < [listOfAll count]; i++)
{
NSMutableDictionary *currentItem = [[NSMutableDictionary alloc] initWithDictionary:[listOfAll objectAtIndex:i]];
//Do something with that current item
}
You can also use that index in your tableView. In that case, you have to use your variable section instead of i to get your desired index.
The second one is using two arrays. Imagine you get an image named imageOne with the text imageName. Then you should use:
NSMutableArray *nameList = [[NSMutableArray alloc] init];
[nameList addObject: imageName];
NSMutableArray *imageList = [[NSMutableArray alloc] init];
[imageList addObject: imageOne];
If you want to use a certain item out of those lists, you just have to use the same indexnumber.
For example:
[theTitleLabel setText:[[NSString alloc] initWithFormat:#"%#", [nameList objectAtIndex:x]]];
[theImageView setImage:[imageList objectAtIndex:x]];
Make sure the x's are the same number.
I understand this is all a lot of information, especially if you're new to Objective - C. A tutorial exists which gives you a lot of information about how to use arrays, dictionaries and table views. As a bonus, you get to know a little about XML-parsing.
I suggest you walk through that tutorial and do everything and read everything it says. This should give you a nice start into the world of programming in iPhones.
Good luck!

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.

Adding 2 keyvalues to list from JSON object

I want to append 2 key values from JSON object to my list in iPhone app. Below is my code for that,
SBJsonParser *jsonParser = [[[SBJsonParser alloc] init] autorelease];
NSString *jsonString=[[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://test/json/json_data.php"]];
id response = [jsonParser objectWithString:jsonString error:NULL];
NSDictionary *feed = (NSDictionary *)response;
list = (NSArray *)[feed valueForKey:#"fname"];
the above code properly displays the value from fname but what do i do if i want to add lname to it. for eg, my object is
[{"fname":"Bill","lname":"Jones"},{"fname":"John","lname":"Jacobs"}]
i want to display names as Bill Jones, John Jacobs and so on in the list. Currently it only displays Bill, John..I tried doing something like #"fname"#lname but it wont work..Can anybody please help me..
An observation: the response from the JSON parser is not a dictionary, but an array given the string you pass in. Your code works because -valueForKey: is something an array will respond to. The array sends -valueforKey: to each element and builds an array out of the responses.
There are two ways you can do what you want (at least)
Iterate through the array explicitly
NSMutableArray* list = [[NSMutableArray alloc] init];
for (id anObject in response)
{
[list addObject: [NSString stringWithFormat: #"%# %#",
[anObject objectForKey: #"fName"],
[anObject objectForKey: #"lname"]]];
}
Add a category to NSDictionary
#interface NSDictionary(FullName)
-(NSString*) fullName;
#end
#implementation NSDictionary(FullName)
-(NSString*) fullName
{
return [NSString stringWithFormat: #"%# %#",
[self objectForKey: #"fName"],
[self objectForKey: #"lname"]];
}
#end
Then your existing code changes to
list = (NSArray *)[feed valueForKey:#"fullName"];

How to persist data using archiving function on the iphone?

I am having trouble with persisting data.
Desired Behavior:
I have a text field defined in the view that captures user input. When the submit button is clicked (touch up inside), I expect to write that data to a mutable array. As of right now, I am going to archive that mutable array upon submit, but have also played with archiving once the view disappears/unloads. When a user navigates away from the view or exits the application and re-enters, I want to display the first element of that mutable array in the textfield.
Actual Behavior:
Textfield captures data and updates myMutableArray upon submitting. I have a few labels on the view plus NSLog to verify the count grows as I hit submit. I then archive this the first time, and I see that my file now exists in myDocuments plus when I revisit the view and check the count of dataArray, which is created if that file exists, the count of dataArray matches the number of elements I created in myMutableArray. If I start to enter in data again, it resets myMutableArray and dataArray all over again. That is one of a couple problems I think I have.
Questions:
If the file exists, then dataArray is created. Now I try to set the textfield to display the first element and the program bombs out! If dataArray exists, and I can see count is some positive value, I don't understand why I can't access that first element.
I feel like when I visit the view and go through this exercise, when I revisit the view I see I have dataArray present and with positive values, but when I start adding text again through the submit button, I reset everything! So I am persisting once maybe, but then wiping it clean. So much for real persistence. What am I doing wrong?
Eventually, I will be storing a record with a few elements. I will create multiple records over time for the user to review. Is archiving the best possible way? Should I start using NSCoding instead?
- (void)viewDidLoad {
[super viewDidLoad];
// Initialize mutable array and set equal to myMutableArray property
NSMutableArray *aMutableArray = [[NSMutableArray alloc] init];
myMutableArray = aMutableArray;
// Debug Logging
// NSLog(#"Mutable Array Count is: %i", [myMutableArray count]);
NSFileManager *filemgr;
NSString *docsDir;
NSArray *dirPaths;
filemgr = [NSFileManager defaultManager];
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the data file
dataFilePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"data.archive"]];
// Check if the file already exists
if ([filemgr fileExistsAtPath: dataFilePath])
{
NSMutableArray *dataArray;
dataArray = [NSKeyedUnarchiver unarchiveObjectWithFile: dataFilePath];
myTextField.text = #"%#", [dataArray objectAtIndex:0]; // THIS BOMBS OUT MY PROGRAM FOR SURE IF INCLUDED!!!
//myUpdatedMutableArray = dataArray;
// Debug Logging
NSLog(#"Mutable Array (dataArray) Count is: %i", [dataArray count]);
//NSLog(#"The first value in the array is: %i", [dataArray objectAtIndex:0]);
}
[filemgr release];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
}
- (IBAction) mySubmitButtonPressed:(id)sender {
// Debug Logging
NSLog(#"Submit Button was Pressed!");
NSLog(#"Mutable Array (myMutableArray) Count is: %i", [myMutableArray count]);
// Create string from textfield and addobject to myMutableArray; check to see that myMutableArray grows in count
NSString *myString = [[NSString alloc] initWithFormat:#"%#",myTextField.text];
[myMutableArray addObject:myString];
NSLog(#"Mutable Array (myMutableArray) Count is: %i", [myMutableArray count]);
// More Debug Logging just using on-screen labels
NSString *mySecondString = [[NSString alloc] initWithFormat:#"%i", [myMutableArray count]];
myFirstLabel.text = myString;
mySecondLabel.text = mySecondString;
// Archive myMutableArray
[NSKeyedArchiver archiveRootObject: myMutableArray toFile:dataFilePath];
//[contactArray release];
}
There are many issues with this code.
To begin, you leak memory all over the place. Specifically here:
NSMutableArray *aMutableArray = [[NSMutableArray alloc] init];
myMutableArray = aMutableArray;
Which should really be:
self.myMutableArray = [NSMutableArray array];
After you unarchive the data, if you want to keep it in myMutableArray, simply do this:
[myMutableArray appendArray: dataArray];
Or just:
self.myMutableArray = dataArray;
You should never release singleton instances like the NSFileManager, so completely get rid of this one:
[fileMgr release];
Your code crashes because this is not valid code:
myTextField.text = #"%#", [dataArray objectAtIndex:0];
(It actually is valid code, but it will do something very different than you think)
This is not Python, so you will have to do this:
myTextField.text = [NSString stringWithFormat: #"%#",
[dataArry objectAtIndex: 0]];
But that is really the same as:
myTextField.text = [dataArray objectAtIndex: 0];
Adding a string to your array in mySubmitButtonPressed can be done much simpler than you think. Just do this:
[myMutableArray addObject: myTextField.text];
You should really learn about autoreleased objects, memory management and what retaining properties actually do.