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
Related
i'm just new to iOS Programming, so appreciate for any help. I'm trying to save text from UITextField to plist and load it in UITableView.
I've managed to make the text not overwritten, but when i try to load it in the table,the app crash.
I know the issue is when my table load the Data.plist since it's array in array.
But I still don't have to clue how to load my plist to table.
This is my code when save the text to plist.
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"Data.plist"];
NSMutableArray *array = [NSMutableArray arrayWithContentsOfFile:plistPath];
if (nil == array) {
array = [[NSMutableArray alloc] init];
}
NSMutableArray *list = [[NSMutableArray alloc]init];
[list addObject:resi.text];
[array addObject:list];
[array writeToFile:plistPath atomically: TRUE];
And this is the whole code for my table view
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
ub;/{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
cell.textLabel.text = [self.array objectAtIndex:indexPath.row];
//cell.textLabel.text = [array objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
}
- (void)viewWillAppear:(BOOL)animated
{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"Data.plist"];
array = [NSMutableArray arrayWithContentsOfFile:plistPath];
[myHistoryTable reloadData];
}
cell.textLabel.text = [self.array objectAtIndex:indexPath.row];
Try this
//As you have NSArrays as objects in self.array.
NSArray *list = (NSArray *)[self.array objectAtIndex:indexPath.row];
if(list && [list count] > 0) { //to check and avoid any crash
cell.textLabel.text = [list objectAtIndex:0];
}
When I make an object class conform to NSCoding, the tableView does not show new entries. Suspected it had to do with the object initialization but cannot solve it. Might be very basic but cannot find the error. Here is the code I simplified to post:
Custom Object:
// DataObject.h
#import <Foundation/Foundation.h>
#interface DataObject : NSObject {
NSString *name;
}
#property (nonatomic, copy) NSString *name;
#end
// DataObject.m
#import "DataObject.h"
#implementation DataObject
#synthesize name;
- (void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:name forKey:#"name"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (!self) return nil;
name = [[decoder decodeObjectForKey:#"name"] retain];
return self;
}
These is the TableView root controller - Have omitted a few methods:
// RootViewController.h
#import <UIKit/UIKit.h>
#interface RootViewController : UITableViewController {
NSMutableArray *list;
}
#property (nonatomic, retain) NSMutableArray *list;
- (void)add:(id)sender;
- (NSString *)dataFilePath;
#end
// RootViewController.m
#import "RootViewController.h"
#import "DataObject.h"
#implementation RootViewController
#synthesize list;
- (void)add:(id)sender
{
DataObject *newEntry = [[DataObject alloc] init];
newEntry.name = #"Willy";
[self.list addObject:newEntry];
[self.tableView reloadData];
// Scroll table view to last row
if ([list count] > 1) {
NSUInteger index = list.count - 1;
NSIndexPath *lastRow = [NSIndexPath indexPathForRow:index inSection: 0];
[self.tableView scrollToRowAtIndexPath: lastRow atScrollPosition: UITableViewScrollPositionTop animated: YES];
}
[newEntry release];
}
- (NSString *)dataFilePath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"datafile"];
}
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.rightBarButtonItem = self.editButtonItem;
self.title = #"Names";
self.list = [[NSMutableArray alloc] init];
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
self.list = tempArray;
UIApplication *app = [UIApplication sharedApplication];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(applicationWillResignActive:)
name:UIApplicationWillResignActiveNotification
object:app];
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
self.navigationItem.rightBarButtonItem = self.editButtonItem;
UIBarButtonItem *addButton = [[UIBarButtonItem alloc]
initWithTitle:#"Add"
style:UIBarButtonItemStylePlain
target:self
action:#selector(add:)];
self.navigationItem.leftBarButtonItem = addButton;
[addButton release];
[self.tableView reloadData];
}
- (void)applicationWillResignActive:(NSNotification *)notification;
{
[NSKeyedArchiver archiveRootObject:self.list toFile:[self dataFilePath]];
}
- (void)viewWillAppear:(BOOL)animated {
[self.tableView reloadData];
[super viewWillAppear:animated];
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [list 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];
}
// Configure the cell.
NSUInteger row = [indexPath row];
DataObject *oneName = [self.list objectAtIndex:row];
cell.textLabel.text = oneName.name;
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete the row from the data source.
NSUInteger row = [indexPath row];
[self.list removeObjectAtIndex:row];
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
#end
Oh, man I am so embarrassed...It had nothing to do with NSCoding directly but when I made the data class NSCoding compliant I added in viewDidLoad:
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
self.list = tempArray;
The first line is fine, but the second assigned a new pointer address to self.list and the tempArray was later autoreleased...
As this line was not executed in the "not NSCoding compliant" version it appeared to work then and not when encodeWithCoder and initWithCoder were implemented.
Because of this self.list was nil instead of being initialized and ready when in the add: method I added a new instance. It was created fine, but never added to the array and therefore never showed in the tableView.
This was easily fixed using instead in viewDidLoad:
NSMutableArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithFile:[self dataFilePath]];
[self.list addObjectsFromArray:tempArray];
Worst part is that I was pulling my hair because of this and was convinced it was because of NSCoding and the initialization of objects...
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.
I have an array of dictionaries in an iOS .plist structured similar to the following:
<plist version="1.0">
<array>
<dict>
<key>name</key>
<string>Afghanistan</string>
<key>government</key>
<string>Islamic Republic</string>
<key>population</key>
<integer>29121286
</integer>
</dict>
<dict>
<key>name</key>
<string>Albania</string>
<key>government</key>
<string>Emerging Democracy</string>
<key>population</key>
<integer>2986952</integer>
</dict>
I am trying to load the <key>name</key> from each dictionary into an NSTableViewCell then display them all alphabetically in an NSTableView similar to the Contacts App in iOS.
Below are my ViewControllers .h and .m. The sort is working, but I am not able to load the results into the TableViewCells?
FirstViewController.h
#import <UIKit/UIKit.h>
#interface FirstViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>
{
NSArray *sortedCountries;
}
#property (nonatomic, retain) NSArray *sortedCountries;
#end
FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize sortedCountries;
-(void)viewDidLoad {
NSString *path = [[NSBundle mainBundle] pathForResource:#"countries"ofType:#"plist"];
NSArray *countries = [NSArray arrayWithContentsOfFile:path];
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES] autorelease];
NSArray *sortedCountries = [[countries sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
}
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
-(NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return 2;
}
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *country = [sortedCountries objectAtIndex:indexPath.row];
NSString *countryName = [country objectForKey:#"name"];
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = countryName;
return cell;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[sortedCountries release];
[super dealloc];
}
#end
EDIT: Another question related to this here.
Add an ivar to your view controller's #interface in the header file:
#interface MyViewController : UITableViewController
{
...
NSArray *sortedCountries;
}
Add this code (to read and sort the plist by country name) to your view controller's initWith... method:
NSArray *countries = [NSArray arrayWithContentsOfFile: pathToPlist];
// Now the array holds NSDictionaries, sort 'em:
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES] autorelease];
sortedCountries = [[countries sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
Then use the following snippet to extract the values:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *country = [sortedCountries objectAtIndex:indexPath.row];
NSString *countryName = [country objectForKey:#"name"];
NSString *governmentType = [country objectForKey:#"government"];
NSSInteger population = [[country objectForKey:#"population"] integerValue];
// ... do something with countryName, governmentType, population
}
Don't forget to release sortedCountries:
- (void)dealloc
{
...
[sortedCountries release];
[super dealloc];
}
Create an NSArray for your file:
NSArray *iOSPlist = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"iOS" ofType:#"plist"]];
then in this method write after if (cell == nil){
}:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
cell.textLabel.text = [[iOSPlist objectAtIndex:indexPath.row] objectForKey:#"name"];
}
and don't forget to return [iOSPlist count] in the - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section method;
Here is an example pulling the version number out of the info.plist. Use something similar to pull out your name key ( objectForKey:#"name")
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"Info.plist"];
plist = [[NSDictionary dictionaryWithContentsOfFile:finalPath] retain];
NSString* version = [plist objectForKey:#"CFBundleVersion"];
Here's a StackOverflow question on working with data in plists. The answers get quite detailed.
Parse Plist (NSString) into NSDictionary
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