I'm just learning iphone development, so please forgive me for what is probably a beginner error. I've searched around and haven't found anything specific to my problem, so hopefully it is an easy fix.
The book has examples of building a table from data hard coded via an array. Unfortunately it never really tells you how to get data from a URL, so I've had to look that up and that is where my problems show up.
When I debug in xcode, it seems like the count is correct, so I don't understand why it is going out of bounds?
This is the error message:
2010-05-03 12:50:42.705 Simple Table[3310:20b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[NSCFArray objectAtIndex:]: index (1) beyond bounds (0)'
My URL returns the following string:
first,second,third,fourth
And here is the iphone code with the book's working example commented out
#import "Simple_TableViewController.h"
#implementation Simple_TableViewController
#synthesize listData;
- (void)viewDidLoad
{
/*
//working code from book
NSArray *array = [[NSArray alloc] initWithObjects:#"Sleepy", #"Sneezy", #"Bashful", #"Bashful", #"Happy",
#"Doc", #"Grumpy", #"Thorin", #"Dorin", #"Norin", #"Ori", #"Balin", #"Dwalin", #"Fili", #"Kili", #"Oin",
#"Gloin", #"Bifur", #"Bofur", #"Bombur", nil];
self.listData = array;
[array release];
*/
//code from interwebz that crashes
NSString *urlstr = [[NSString alloc] initWithFormat:#"http://www.mysite.com/folder/iphone-test.php"];
NSURL *url = [[NSURL alloc] initWithString:urlstr];
NSString *ans = [NSString stringWithContentsOfURL:url];
NSArray *listItems = [ans componentsSeparatedByString:#","];
self.listData = listItems;
[urlstr release];
[url release];
[ans release];
[listItems release];
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload
{
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.listData = nil;
[super viewDidUnload];
}
- (void)dealloc
{
[listData release];
[super dealloc];
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.listData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:SimpleTableIdentifier] autorelease];
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [listData objectAtIndex:row];
return cell;
}
#end
YOu are overreleasing listItems as well as ans, that is probably your problem...WHen you dont do an alloc or retain (usually) the objects returned are autoreleased, therefore you dont need to release them. [NSString ...] returns an auto released string and so does the call [ans componentsSeparatedByString:#","]...hope that helps
Related
I've been learning Objective C and the iPhone SDK gradually, and my current project involves building an application that stores numerical data (sports data). This is primarily for learning purposes, as there are multiple apps that do the same thing. Anyway, I've hit a bit of a snag. My intention is to have a list of players stored in a table and allow the user to add additional players.
At the moment, I have a button that when pressed, "Ninjas" will be added to the table. I've also enabled deleting in the table. Unfortunately, I can't seem to figure out how to save and load the data from a plist. I followed various tutorials and guides, but I can't figure out what's happening. My suspicion is I am loading the data from an empty array and adding to that array, but the array involving the data is a separate array from the plist. Unfortunately, I am a bit lost beyond that.
The data in my array is erased whenever I switch views. However, I've noticed that the data remains if I leave and come back, but not if I leave for a considerable amount of time, leave and restart the iphone, etc. This seems to occur even for apps that I have not worked on saving. Is this just a function of the iPhone holding onto data in case a user accidentally exits a program?
Hopefully I explained my issue somewhat tangibly. TL:DR version: I want to add data to an array, save it to a plist, and reload the data from the plist whenever the array is present on the screen. Code below is attempting to accomplish this, but it isn't succeeding.
Thanks
#import "RootViewController.h"
#import "NewPlayer.h"
#import "OptionsMenu.h"
#implementation RootViewController
#synthesize createdPlayers;
#synthesize listOfPlayers;
-(NSString *) pathOfFile{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [paths objectAtIndex:0];
return [documentFolder stringByAppendingFormat:#"myfile.plist"];
}
-(void)applicationWillTerminate:(NSNotification*)notification{
NSMutableArray *array = [[NSMutableArray alloc]init];
[array writeToFile: [self pathOfFile] atomically:YES];
}
- (void)viewDidLoad
{
NSString *filePath = [self pathOfFile];
if ([[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
NSArray *array = [[NSArray alloc]initWithContentsOfFile:filePath];
listOfPlayers.array = [array objectAtIndex:0];
[array release];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:app];
[super viewDidLoad];
listOfPlayers = [[NSMutableArray alloc] init];
}
-(IBAction)AddButtonAction:(id)sender{
[listOfPlayers addObject:#"Ninjas"];
[createdPlayers reloadData];
}
-(IBAction)switchView:(id)sender{
OptionsMenu *second = [[OptionsMenu alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:second animated:YES];
}
-(IBAction)newView:(id)sender{
NewPlayer *second = [[NewPlayer alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:second animated: YES];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [listOfPlayers count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
cell.textLabel.font = [UIFont fontWithName:#"Helvetica" size:15];
cell.textLabel.textColor = [UIColor whiteColor];
cell.textLabel.textAlignment = UITextAlignmentCenter;
NSString *cellValue = [listOfPlayers objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
NSLog(#"delete section: %d rol: %d", [indexPath indexAtPosition:0], [indexPath indexAtPosition:1]);
[listOfPlayers removeObjectAtIndex:[indexPath indexAtPosition:1]];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
else if (editingStyle == UITableViewCellEditingStyleInsert)
{
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view.
}
}
-(void)tableView:(UITableView *)listOfPlayers moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath{
}
-(void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc{
[createdPlayers release];
[listOfPlayers release];
[super dealloc];
}
#end
#import <UIKit/UIKit.h>
#interface RootViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView* createdPlayers;
IBOutlet UIButton* superCat;
NSMutableArray *listOfPlayers;
}
#property(nonatomic, retain) IBOutlet NSObject *listOfPlayers;
-(NSString *) pathOfFile;
-(void)applicationWillTerminate:(NSNotification*)notification;
-(IBAction)AddButtonAction:(id)sender;
-(IBAction)switchView:(id)sender;
-(IBAction)newView:(id)sender;
#property (nonatomic, retain) UITableView* createdPlayers;
#end
Code update 20th Dec:
-(NSString *) pathOfFile{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [paths objectAtIndex:0];
return [documentFolder stringByAppendingFormat:#"myfile.plist"];
}
-(void)applicationWillTerminate:(NSNotification*)notification
{
[self.listOfPlayers writeToFile:[self pathOfFile] atomically:YES encoding:NSUTF8StringEncoding error:NULL];
}
- (void)viewDidLoad
{
self.listOfPlayers = [[NSMutableArray alloc] init];
NSString *filePath = [self pathOfFile];
if ([[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
NSArray *array = [[NSArray alloc]initWithContentsOfFile:filePath];
self.listOfPlayers = array;
[array release];
}
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(applicationWillTerminate:) name:UIApplicationWillTerminateNotification object:app];
[super viewDidLoad];
}
I don't know plist. But you can try use NSData
NSArray *array;
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
//Write data
[data writeToFile:filePath atomically:YES];
//Read data
NSFileManager *fileManager = [NSFileManager defaultManager];
NSData *oldData = [fileManager contentsAtPath:filePath];
NSArray *newArray = [NSKeyedUnarchiver unarchiveObjectWithData:oldData]
Welcome to the world of iOS programming. It's a lot of fun, but it can be frustrating, just as programming in any language can be. I feel your pain.
There are a number of things that I see:
In viewDidLoad, it appears that your code to read your file into an array is okay, but then it appears as if you're trying to assign only the first row of this array to your listOfPlayers. Try:
self.listOfPlayers = array;
array is an array, and so is your listOfPlayers. Also, using self will ensure that the synthesized setter is used, thus retaining your listOfPlayers when you release array.
In applicationWillTerminate, I believe you wish to write your listOfPlayers to a file, but what you are actually doing is allocating and initializing an empty array, and writing THAT to your file. Try:
-(void)applicationWillTerminate:(NSNotification*)notification
{
[self.listOfPlayers writeToFile: [self pathOfFile] atomically:YES];
}
I hope this helps!
There seems something wrong in your viewDidLoad
if ([[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
NSArray *array = [[NSArray alloc]initWithContentsOfFile:filePath];
listOfPlayers.array = [array objectAtIndex:0];
.
.
}
.
.
.
listOfPlayers = [[NSMutableArray alloc] init];
You are accessing listOfPlayers before ever allocating it.
listOfPlayers.array is wrong as well
my question depends on my other question
NSMutableArray vs NSArray
i have created a navigationController and load a TableView inside with data from the other question. Now a get a detailview and get new data from xml, so i copy my methods and modifide them.
but it is the same stucture, i does not change a lot.
But now i get the same error.
i have in detailview.h
NSMutableArray *seminareArray;
and
#property (nonatomic, retain) NSMutableArray *seminareArray;
in detailview.m
#synthesize SeminareListeTabelle, selectedSeminar, seminareArray, receivedData;
i add this code
seminareArray = [[NSMutableArray alloc] init];
self.seminareArray = [NSMutableArray arrayWithCapacity:10];
before i add the data. and i get the error here
cell.textLabel.text = [seminareArray objectAtIndex:row];
EXC_BAD_ACCESS again some type problem
i add data to array like this
if([elementName isEqualToString:#"seminar"])
{
//NSLog(#"%#", [attributeDict objectForKey:#"name"]);
NSString *seminarName = [NSString stringWithFormat:#"%#", [attributeDict objectForKey:#"name"]];
[seminareArray addObject:seminarName];
[seminarName release];
}
the array is filled with data, but after tableView reload, i get this error.
//
// SeminareListingView.m
// Seminar App2
//
// Created by Alexander Frischbutter on 05.07.11.
// Copyright 2011 __MyCompanyName__. All rights reserved.
//
#import "SeminareListingView.h"
//#import "SeminareView.h"
#implementation SeminareListingView
#synthesize SeminareListeTabelle, selectedSeminar, seminareArray, receivedData;
- (void) parseData:(NSString *)url
{
if(receivedData)
{
receivedData = nil;
}
NSLog(#"Parsing... url: %#", url);
NSURLRequest *theRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:#"%#", url]] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if(theConnection)
{
receivedData = [[NSMutableData data] retain];
}
else
{
//label.text = #"XML nicht geladen";
NSLog(#"XML nicht gefunden");
}
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
SeminareListeTabelle = [[UITableView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] style:UITableViewStylePlain];
SeminareListeTabelle.delegate = self;
SeminareListeTabelle.dataSource = self;
SeminareListeTabelle.autoresizesSubviews = YES;
seminareArray = [[NSMutableArray alloc] init];
self.seminareArray = [NSMutableArray arrayWithCapacity:10];
[self parseData:[NSString stringWithFormat:#"http://akademie.kunden.fincha.com/semapp/sem_kat_arbtechnik.xml", selectedSeminar]];
self.navigationItem.title = [NSString stringWithFormat:#"%#", selectedSeminar];
self.view = SeminareListeTabelle;
// Do any additional setup after loading the view from its nib.
}
- (void)startParsingData
{
NSLog(#"Parsing started");
NSXMLParser *dataParser = [[NSXMLParser alloc] initWithData:receivedData];
dataParser.delegate = self;
[dataParser parse];
[dataParser release];
[receivedData release];
NSLog(#"Received Data in seminareArray");
/*
for(int i = 0; i < [seminareArray count]; i++)
{
NSLog(#"%d is %#", i, [seminareArray objectAtIndex:i]);
//NSLog(#"Count %d", [kategorienArray count]);
}
*/
//[seminareArray release];
NSLog(#"Reload data in TableView");
[self.SeminareListeTabelle reloadData];
NSLog(#"Data reloaded");
}
- (void)viewDidUnload
{
//[seminareArray release];
//[SeminareListeTabelle release];
NSLog(#"Vew unloaded");
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *SimpleTableIdentifier = #"SimpleTableIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: SimpleTableIdentifier];
if (cell == nil) { cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:SimpleTableIdentifier] autorelease];
}
if([seminareArray count] != 0)
{
NSLog(#"Adding data to cell");
NSUInteger row = [indexPath row];
//cell.textLabel.text = [NSString stringWithFormat:#"bla, %d", row]; //[seminareArray objectAtIndex:row];
cell.textLabel.text = [seminareArray objectAtIndex:row];
NSLog(#"Added data to cell");
}
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//NSLog(#"Count %d", [self.seminareArray count]);
return [seminareArray count];
}
-(NSInteger) tableView:(UITableView *)tableView indentationLevelForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 0;
}
//Anzeige mit Seminaren öffnen bei Click auf die Zeile
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//gehe zurück zum ersten View
//NSLog(#"Received Data in seminareArray");
[[self navigationController] popViewControllerAnimated:YES];
}
- (void)connection:(NSURLConnection *)connection didReceiveResonse:(NSURLResponse *)response
{
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if(receivedData)
{
[receivedData appendData:data];
}
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[connection release];
[receivedData release];
//label.text = #"Connection failed";
NSLog(#"Verbindung fehlgeschlagen!");
//[[self navigationController] popViewControllerAnimated:YES];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[self startParsingData];
[connection release];
}
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{
//NSLog(#"Parser was called. Element: %#", elementName);
if([elementName isEqualToString:#"seminar"])
{
//NSLog(#"%#", [attributeDict objectForKey:#"name"]);
NSString *seminarName = [NSString stringWithFormat:#"%#", [attributeDict objectForKey:#"name"]];
[seminareArray addObject:seminarName];
[seminarName release];
}
}
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
{
NSLog(#"Parse Error %#", parseError);
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
The problem stems from this code:
seminareArray = [[NSMutableArray alloc] init]; // owned
seminareArray = [NSMutableArray arrayWithCapacity:10]; // autoreleased
You're first initializing the semiareArray as an owned object, but then are re-setting it as an autoreleased object.
Meaning, it will be released after the run-loop terminates.
Remove the second (autoreleased) statement but keep the first, and everything should work fine.
The reason why you're getting the EXC_BAD_ACCESS error is because the seminareArray object is released at some point before it being used again.
Additionally, try to debug your
cell.textLabel.text = [seminareArray objectAtIndex:row];
Try setting it as id var = [seminareArray objectAtIndex:row]; and then setting cell.textLabel.text = var; This will tell you if the error occurs due to array being dealloc'd too early, or improper cell/textLabel.
Updated:
There's an additional problem is with the code:
NSString *seminarName = [NSString stringWithFormat:#"%#", [attributeDict objectForKey:#"name"]];
[seminareArray addObject:seminarName];
[seminarName release]; // <--
You're creating an auto-released object seminarName, which technically has retain count 0. You're adding it to the semiareArray, which ups the object retain count to 1. Then you're releasing it again. Which causes it to be dealloc'd at runtime. The problem is that when you're assigning the value to the textLabel, the object no longer exists.
Solution: remove the [seminarName release]; Don't worry about releasing the seminarName, since it's auto-released, it will be released when the array is dealloc'd, or when the object it removed from the array.
David's answer is correct but I would advice reading up on your memory management.
If you are synthesizing properties then it is a lot easier to use the getters and setters and let them do the memory management for you. The exception being in your init/dealloc methods where you should try to directly use the ivars to avoid any potential side effects of using the getters/setters.
With the two lines david highlighted
seminareArray = [[NSMutableArray alloc] init]; // owned
seminareArray = [NSMutableArray arrayWithCapacity:10]; // autoreleased
You could potentially use either if the memory management was done correctly.
The first line on its own is correct as it creates an instance of NSMutableArray with a retain count of +1 then assigns it straight to the ivar.
Then as David pointed out the second line replaces this with an autoreleased NSMutableArray so this line is superflous and crashes your program. The method arrayWithCapacity: is not simply setting the capacity of the array it is giving you a new autoreleased array.
If you wanted to use an autoreleased NSMutableArray then you would need to use the setter either with dot notation of passing a message:
self.seminareArray = [NSMutableArray arrayWithCapactiy:10];
OR
[self setSeminareArray:[NSMutableArray arrayWithCapcity:10]];
By simply referencing things straight to seminareArray you are avoiding the getters/setters you synthesized and therefore are responsible for all of your memory management.
A hint at a memory leak:
self.seminareArray = [[NSMutableArray alloc] init];
this will leak memory because seminare is declared as retained property:
#property (nonatomic, retain) NSMutableArray *seminareArray;
This is not, anyway, the cause of your other issue.
The error you are having is caused by row being greater that the count of your array. So either you don't add sufficient objects to the array, or you try to use an incorrect value for row. Inspect that line with a debugger and ensure that row never goes beyond [seminare count]; you will find out easily why it happens.
hoping someone can help me.
In the didSelectRowAtIndexPath method, I am loading and pushing detail view controller, when you tap the "back" button in the detail view to go back to the root view, the app will crash. No error logs or anything.
I'm 95% sure it's got something to do with the "rides" object being released too early, but can't figure it out.
Thanks for your help!
#import "RidesViewController.h"
#import "RideDetailViewController.h"
#import "JSON.h"
#implementation RidesViewController
#synthesize rides;
#pragma mark -
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"table-view-background.png"]];
rides = [[NSDictionary alloc] init];
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"rides" ofType:#"JSON"];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSString *responseString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSDictionary *results = [responseString JSONValue];
rides = [results objectForKey:#"rides"];
[rides retain];
}
#pragma mark -
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [rides count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
cell.textLabel.text = [[rides valueForKey:#"title"] objectAtIndex:indexPath.row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
RideDetailViewController *rideDetailViewController = [[RideDetailViewController alloc] initWithNibName:#"RideDetailViewController" bundle:nil];
NSString *aTitle = [[NSString alloc] initWithString:[[rides valueForKey:#"title"] objectAtIndex:indexPath.row]];
NSString *aImagePath = [[NSString alloc] initWithString:[[rides valueForKey:#"imagePath"] objectAtIndex:indexPath.row]];
NSString *aDescription = [[NSString alloc] initWithString:[[rides valueForKey:#"description"] objectAtIndex:indexPath.row]];
NSString *aVideoPath = [[NSString alloc] initWithString:[[rides valueForKey:#"videoPath"] objectAtIndex:indexPath.row]];
[rideDetailViewController initWithTitle:aTitle imagePath:aImagePath description:aDescription videoPath:aVideoPath];
[self.navigationController pushViewController:rideDetailViewController animated:YES];
[rideDetailViewController release];
[aTitle release];
[aImagePath release];
[aDescription release];
[aVideoPath release];
}
#pragma mark -
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)dealloc {
[super dealloc];
}
#end
Define your NSString's in your header, then place them in cellForRowAtIndexPath:
just Cut whole thing from viewdidload and paste this on viewwillapper method.
I'm trying to finish an app but I'm having some memory leaks with CoreData when I delete the view from the navigation stack even though I released everything I created.
Basically the following method is called by the view below it.
+ (NSMutableArray *)getStoriesForSubscription:(Subscriptions *)s {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *storiesEntity = [NSEntityDescription entityForName:#"Articles" inManagedObjectContext:ikub.context];
[request setEntity:storiesEntity];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(belongsTo == %#)", s];
[request setPredicate:predicate];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"pubDate" ascending:NO selector:nil];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[request setSortDescriptors:sortDescriptors];
NSError *error = nil;
NSMutableArray *stories = (NSMutableArray*)[ikub.context executeFetchRequest:request error:&error];
if (![ikub.context save:&error]) { NSLog(#"Cannot fetch the folders from the fetch request."); }
[sortDescriptors release];
[sortDescriptor release];
[request release];
return stories;
}
#implementation SubscriptionStories
#synthesize storiesTable, stories, subscription;
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [stories count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"];
}
int index = [indexPath row];
cell.textLabel.text = [[stories objectAtIndex:index] title];
cell.detailTextLabel.text = [[stories objectAtIndex:index] desc];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
if ([[[stories objectAtIndex:index] read] isEqualToNumber:[NSNumber numberWithBool:NO]]) {
cell.textLabel.textColor = [UIColor blackColor];
} else {
cell.textLabel.textColor = [UIColor grayColor];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
StoryDetails *details = [[StoryDetails alloc] init];
details.title = #"Detaje";
details.t = [[stories objectAtIndex:[indexPath row]] title];
details.d = [[stories objectAtIndex:[indexPath row]] desc];
details.l = [[stories objectAtIndex:[indexPath row]] link];
details.g = [[stories objectAtIndex:[indexPath row]] guid];
details.p = (NSString *)[[stories objectAtIndex:[indexPath row]] pubDate];
[SubscriptionsController setStoryAsRead:[[stories objectAtIndex:[indexPath row]] link] forSubscription:subscription];
[self.navigationController pushViewController:details animated:YES];
[details release];
}
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
stories = [[SubscriptionsController getStoriesForSubscription:subscription] retain];
[storiesTable reloadData];
}
- (void)viewWillDisappear:(BOOL)animated {
[stories release];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[subscription release];
[super dealloc];
}
Instruments says that the leak happens in this line:
stories = [[SubscriptionsController getStoriesForSubscription:subscription] retain];
If you have declared the property stories with retain then the extra retain is not necessary.
self.stories = [SubscriptionsController getStoriesForSubscription:subscription];
my suggestions:
remove (another?) leak by changing your UITableViewCell creation to return an autoreleased cell
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"cell"] autorelease];
...
}
if that didn't help. (I had leaks were instruments was miles away from the actual leak). change your viewWillDisappear method to something like this
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[stories release];
stories = nil;
}
and add another release for stories in dealloc
- (void)dealloc {
[subscription release];
[stories release];
[super dealloc];
}
Maybe there are obscure ways that a dealloc happens without calling the viewWillDisappear: method.
I usually release everything in dealloc. As long as you make sure that you set an object to nil when you have released it in another method nothing bad will happen.
The leak was in a totally different place. Instruments isn't always right.
What did I do wrong? I just modified the Navigation Based application code a bit to read and display a JSON string. It crashes when I scroll up the list with the message Objc_msgSend and points at this as the problem: cell.textLabel.text=[[locations objectAtIndex: storyIndex] objectForKey: #"title"];
#import "RootViewController.h"
#import "JSON.h"
#implementation RootViewController
#synthesize locations;
- (NSString *)stringWithUrl:(NSURL *)url
{
// Construct a String around the Data from the response
return [[NSString alloc] initWithContentsOfURL:url];
}
-(void)jsonLoad {
NSURL *URL = [NSURL URLWithString:#"http://bombaytokyo.com/whrru/jsonexample.html"];
NSString *jsonstring = [self stringWithUrl:URL];
NSLog(jsonstring);
locations = [jsonstring JSONValue];
[jsonstring release];
}
- (void)viewDidLoad {
[super viewDidLoad];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem;
[self jsonLoad];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
[locations release];
}
- (void)viewDidUnload {
// Release anything that can be recreated in viewDidLoad or on demand.
// e.g. self.myOutlet = nil;
}
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// return 0;
return [locations count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
int storyIndex = [indexPath indexAtPosition: [indexPath length] - 1];
NSLog(#"Index Path: %i",indexPath);
//NSLog(title);
cell.textLabel.text=[[locations objectAtIndex: storyIndex] objectForKey: #"title"];
return cell;
}
- (void)dealloc {
[super dealloc];
}
#end
0 based arrays, id length is 0 the 0 - 1 = -1 this will obviously cause an exception.
[indexPath indexAtPosition: [indexPath length] - 1]
change to
[indexPath indexAtPosition: [indexPath length]]
and are you sure you dont want indexPath.row. it looks like you are just returning the length each time.
solution:
self.locations = [jsonstring JSONValue];
Thanks for all the suggestions.