NSArray causing memory leak when simulated low memory warning - iphone

I have made a sample project that reproduces this issue which contains two views:
root header:
#import <UIKit/UIKit.h>
#import "view2.h"
#interface RootViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>{
view2 *secondView;
UITableView *table;
NSArray *array;
}
#property (nonatomic, retain) view2 *secondView;
#property (nonatomic, retain) IBOutlet UITableView *table;
#property (nonatomic, retain) NSArray *array;
#end
root main:
#import "RootViewController.h"
#implementation RootViewController
#synthesize table, array, secondView;
- (void)viewDidLoad
{
[super viewDidLoad];
if(self.array == nil){
self.array = [NSArray arrayWithObjects:#"1", #"2", #"3", #"4", nil];
}
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload
{
[super viewDidUnload];
table = nil;
array = nil;
secondView = nil;
}
- (void)dealloc
{
[table release];
[array release];
[secondView release];
[super dealloc];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [array 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 = [array objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (secondView == nil) {
secondView = [[view2 alloc] init];
}
[self.navigationController pushViewController:secondView animated:YES];
}
#end
view2 simple contains a label with the text "view 2" for identification purposes.
All this code is doing in the root controller is creating an array with the values 1,2,3,4 and binding this text as rows to the table, clicking any row pushes view 2 onto the stack.
if you load up the app in the simulator using the leaks instruments tool, click on any row so that view2 is displayed and then simulate an error warning the following leaks appear:
image
for the line:
self.array = [NSArray arrayWithObjects:#"1", #"2", #"3", #"4", nil];
this is causing me a lot of problems in my main app as i am using arrays to provide data in tables all over the place.
i have tried various ways to fix this such as declaring the array in different ways to no avail.
any help is greatly appreciated!
thanks

In viewDidUnload you're mixing up property vs. direct ivar access.
array = nil simply sets the ivar to nil without using the synthesized accessor method. You have to use the dot notation: self.array = nil;
This way the accessor setArray: is used which handles memory management for you.
Mixing up ivars and properties is a frequent problem amongst Objective-C beginners. The confusion can easily be prevented by always using different names for properties and ivars:
#synthesize array = _array;
You can just leave out the ivar declaration in the class's #interface or name it as in the #synthesize directive.

Related

Passing Data from Dynamic tableView to Static TableView

Hi Let me try to clarify my issue. I have two TableViews, one is static and the other is dynamic. The static= RootVC and Dynamic=FirstVC. In FirstVC i have data that I want to select,save and pass the saved data to a UILabel in RootVC. 1)When I run my App data is selected however it is not saved or passed to my rooVC. I was using delegates and was advice not to use "delegate" but use "Blocks". But still i'm facing the same issue. Here is my code:
in rootVC.h
#import <UIKit/UIKit.h>
#interface RootViewController : UITableViewController
{
NSString *getRepeatLabel;
}
#property (strong, nonatomic) IBOutlet UILabel *repeatLabel;
#property (strong, nonatomic) IBOutlet UILabel *repeatDetail;
#property (nonatomic,strong) NSString *getRepeatLabel;
#end
in my rootVC.m
#import "RootViewController.h"
#interface RootViewController ()
#end
#implementation RootViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_repeatLabel.text = #"Repeat";
_repeatDetail.text = getRepeatLabel;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationController = segue.destinationViewController;
if( [destinationController isKindOfClass:[FirstViewController class]] )
{
[(FirstViewController *)destinationController setCompletionBlock:^(NSString *getRepeatLabel;)
{
// do something here with your string // maybe you must reload your table // it depends on where your returning data needs to display <--------Not sure what to do here
// NSDateFormatter*dateFormatter = [[NSDateFormatter alloc]init];
// NSArray*days = [dateFormatter shortWeekdaySymbols]; <------Here I would like when data is selected to show days in short symbol
NSLog (#"The selected day/s is %#", getRepeatLabel); <---nothing displaying on console
}];
}
}
#end
in FirstVC.h
#import <UIKit/UIKit.h>
#import "RootViewController.h"
typedef void(^WeekdayCompletionBlock)(NSString *dayName);
#interface FirstViewController : UITableViewController
{
NSString *dayName;
}
#property (nonatomic, strong) WeekdayCompletionBlock completionBlock;
#property (nonatomic,strong) NSString *dayName;
- (IBAction)save:(id)sender;
#end
in FirstVC.m
#import "FirstViewController.h"
#import "RootViewController.h"
#interface FirstViewController ()
#end
#implementation FirstViewController
#synthesize completionBlock;
#synthesize dayName;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Initialize table data
completionBlock = [NSArray arrayWithObjects:#"Sunday", #"Monday", #"Tuesday", #"Wednesday", #"Thursday", #"Friday", #"Saturday", nil];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [completionBlock count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"RepeatCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = [completionBlock objectAtIndex:indexPath.row];
return cell;
}
// Called after the user changes the selection.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSLog (#"The selected day/s is %#", [completionBlock objectAtIndex:indexPath.row]);
_getRepeatLabel = completionBlock; //<-----------string from RootVC gives error "undeclared _getRepeatLabel"
}
- (IBAction)save:(id)sender
{
NSUserDefaults *myNewWeekString = [NSUserDefaults standardUserDefaults];
[myNewWeekString setObject:completionBlock forKey:#"%#"];
[myNewWeekString synchronize];
self.completionBlock(myNewDayOfWeekString) <------error myNewDayOfWeekString undeclared and if i declare it here it complains about incompatibility
}
#end
Your code is a little bit wrong. I think you don't really understand block.
You want to pass more than string so the best way to do that is via array. Change block definition to accept array instead of string:
typedef void(^WeekdayCompletionBlock)(NSArray *dayName);
Change declaration of your property in FirstVC.h to:
#property (nonatomic, copy) NSArray *completionBlock; //This is your array, you use it as data source, It's not a block
//Add your block property
// This is your block property you will use it to pass the data between view controllers
#property (copy) WeekdayCompletionBlock returnBlock;
//Add property to keep your selected days
#property (nonatomic, strong) NSMutableArray *returnArray;
Add this line to viewDidLoad method:
self.returnArray = [[NSMutableArray alloc] init];
Change your didSelectRowAtIndexPath method in FirstVC.m to:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (cell.accessoryType == UITableViewCellAccessoryCheckmark)
{
cell.accessoryType = UITableViewCellAccessoryNone;
//remove data from array
[self.returnArray removeObject:[completionBlock objectAtIndex:indexPath.row]];
}
else
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
//add data to array
[self.returnArray addObject:[completionBlock objectAtIndex:indexPath.row]];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
In your save: method call block and pass value to rootVC replace line:
self.completionBlock(myNewDayOfWeekString);
with:
if (self.returnBlock)
{
self.returnBlock(self.returnArray);
}
[self.navigationController popViewControllerAnimated:YES];
The last change left to do is set up ablok in your rootVC.m file. Replace line:
[(FirstViewController *)destinationController setCompletionBlock:
with (nsstring needs to be replaced with nsarray - you pass array with all of the selected data)
[(FirstViewController *)destinationController setCompletionBlock:^(NSArray *getRepeatLabel)
You set up block not NSArray.
I don't know what are you trying to do here:
[myNewWeekString setObject:completionBlock forKey:#"%#"];
You are using %# as a key. It should be text for example #"MY_KEY_FOR_ACCESING_DAYSOFWEEK". You are saving completionBlock it's all of your days if you want to save just selected days replace it with self.returnArray.
Hope this help.

Where/If to release properties in a UITableViewCell

EDIT: Ok, so i figured out how to remedy my original problem, but i'm not sure if this is the best way.
My new question is, say I have a subclass of UITableViewCell with the following property declaration in the header:
#property (nonatomic, retain) IBOutlet UILabel *levelLabel;
This is connected in IB. Is it ok to not release this in dealloc, and not release it at all? This is the only way I can figure out to get it to work, without giving me an exc_bad_access error. Before, it called dealloc when the tableviewcell went off the screen but then it still needed it. where do i release stuff, or does it take care of that for me?
Original Title: Memory leak in UITableView and exc_bad_access
Ok, I am confused. I was following along with this tutorial online making custom UITableViewCells. I made one, and i did everything like the tutorial told me. My UITableViewCell subclass contains 3 UILabels and 3 UIButtons, and has all of them defined as properties and connected in IB. I need them to be available to the class because i need to know when the buttons are pressed and be able to change the text. When I run the app, i start scrolling and after a few seconds it crashes, with exc_bad_access in main (no output in the console). But when I run the app in instruments with NSZombieEnabled, it does not crash at all, and runs just fine. However, since instruments shows you the allocations, i can see them going up very quickly, especially as I scroll. I dont know if this is all allocations, or if these are being released, but still it seems too fast.
Here is PointCoordinatesCell.h (my custom cell):
#import <UIKit/UIKit.h>
#interface PointCoordinatesCell : UITableViewCell
#property (nonatomic, retain) IBOutlet UILabel *levelLabelLabel;
#property (nonatomic, retain) IBOutlet UILabel *levelLabel;
#property (nonatomic, retain) IBOutlet UILabel *levelDescriptionLabel;
#property (nonatomic, retain) IBOutlet UIButton *beginningButton;
#property (nonatomic, retain) IBOutlet UIButton *developingButton;
#property (nonatomic, retain) IBOutlet UIButton *secureButton;
#end
PointCoordinatesCell.m:
#import "PointCoordinatesCell.h"
#implementation PointCoordinatesCell
#synthesize levelLabel, levelLabelLabel, levelDescriptionLabel, beginningButton, developingButton, secureButton;
- (void)dealloc{
[super dealloc];
[levelLabel release];
[levelLabelLabel release];
[levelDescriptionLabel release];
[beginningButton release];
[developingButton release];
[secureButton release];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
#end
RootViewController.h has nothing in it other than a class declaration and standard imports. No variables or methods defined. It subclasses UITableViewController.
RootViewController.m:
#import "RootViewController.h"
#import "StatesAppDelegate.h"
#import "PointCoordinatesCell.h"
#implementation RootViewController
- (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;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
#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 50;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
return 293;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"PointCoordinatesCell";
PointCoordinatesCell *cell = (PointCoordinatesCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"PointCoordinatesCell" owner:self options:nil];
for (id currentObject in topLevelObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (PointCoordinatesCell *) currentObject;
break;
}
}
}
//cell.capitalLabel.text = [capitals objectAtIndex:indexPath.row];
//cell.stateLabel.text = [states objectAtIndex:indexPath.row];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
// AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:#"AnotherView" bundle:nil];
// [self.navigationController pushViewController:anotherViewController];
// [anotherViewController release];
}
- (void)dealloc {
[super dealloc];
}
#end
It seems you are doing some complicated casting in your cellForRowAtIndexPath method. I do not think this is necessary. It does not seem logical to me to check for an object of class UITableViewCell and then cast it into a custom cell. The cell in your nib should already be a custom cell.
In the Apple sample the loading of the cell is much more straight forward. You link your custom cell to an IBOutlet in your view controller and then do this:
CustomCell *cell = (CustomCell *) [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"CustomCell" owner:self options:nil];
cell = customTableViewCell;
self.customTableViewCell = nil;
// etc.
}
No, it is not okay to not release the label. You declared the property with 'retain'-specifier. That means you will have to release it at least in dealloc (A safe way to do that is: self. levelLabel = nil;).
As you've noticed the memory consumption will raise during scrolling if you don't release the objects (memory leaks!).
You'll have to tell where the exc_bad_access error occurs so that we can help you ...

dimissModalViewControllerAnimated causing tableView to be full screen?

I have a RootViewController with 2 tableViews as subviews (created in IB) each with their own tableViewController class (handleing fetchRequests etc.)
1 tableView is static (no data changed by user or modelViews).
tableView 2 has a button in the header which presents an imagePickerController.
No issues so far.
Problem is, when i dismiss the imagePicker
[self dismissModalViewControllerAnimated:YES];
TableView 2 becomes full screen i have tried
[[self rootViewController] dismissModalViewControllerAnimated:YES]
Nothing happens at all. It sticks on the image picker.
I suspect this is due to there being very little of the view being created programmaticaly.
Any ideas?
Thanks in advance.
DetartrateD
-(IBAction)addImageTableAPressed {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
imagePicker.delegate = self;
[self presentModalViewController:imagePicker animated:YES];
[imagePicker release];
}
RootViewController
|| ||
|| ||
\/ \/ addImageTableAPressed
TableViewControlA TableViewControlB --------------------->modelViewController
To resolve mananagedObjectContect.....
- (void)viewDidLoad {...
if(managedObjectContext == nil)
{
managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSLog(#"After managedObjectContext: %#", managedObjectContext);
}
...
}
As I mentioned in one of my comments, I would prefer having a single view controller managing the two table views. Define a UIView (the rootView) including 2 subviews (tableViewA and tableViewB). Your RootViewController's view will be rootView, and this controller will have to be the data source and delegate of both table views. The code I will give here is by no means complete nor optimal, but gives you a good idea of what is needed to implement my solution.
For example:
#interface RootViewController <UITableViewDelegate, UITableViewDataSource> {
NSArray *dataArrayA;
NSArray *dataArrayB;
UITableView tableViewA;
UITableView tableViewB;
NSManagedObjectContext *context;
}
#property (nonatomic, retain) NSArray *dataArrayA;
#property (nonatomic, retain) NSArray *dataArrayB;
// in IB, link the dataSource and delegate outlets of both tables to RootViewController
#property (nonatomic, retain) IBOutlet UITableView tableViewA;
#property (nonatomic, retain) IBOutlet UITableView tableViewB;
// this property will allow you to pass the MOC to the RootViewController from
// the parent view controller, instead of accessing the app delegate from RootViewController
#property (nonatomic, retain) NSManagedObjectContext *context;
// ... etc.
#end
#implementation RootViewController
#synthesize dataArrayA;
#synthesize dataArrayB;
#synthesize tableViewA;
#synthesize tableViewB;
#synthesize context;
// initialize dataArrayA and dataArrayB
- (void)viewDidLoad {
[super viewDidLoad];
NSError *error = nil;
// initialize and configure your fetch request for data going into tableViewA
NSFetchRequest fetchRequestA = [[NSFetchRequest alloc] init];
// configure the entity, sort descriptors, predicate, etc.
// ...
// perform the fetch
self.dataArrayA = [context executeFetchRequest:fetchRequestA error:&error];
// do the same for the data going into tableViewB - the code is very similar, you
// could factor it out in a private method instead of duplicating it here
// NSFetchRequest fetchRequestB = [[NSFetchRequest alloc] init];
// omitting the details ... etc.
self.dataArrayB = [context executeFetchRequest:fetchRequestB error:&error];
// release objects you don't need anymore, according to memory management rules
[fetchRequestA release];
[fetchRequestB release];
}
// Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// if you have a different number of sections in tableViewA and tableViewB
/*
if (tableView == tableViewA) {
return ??;
} else {
return ??
}
*/
// otherwise, if both table views contain one section
return 1;
}
// Customize the number of rows in each table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == tableViewA) {
return [dataArrayA count];
} else {
return [dataArrayB count];
}
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (tableView == tableViewA) {
// get the data for the current row in tableViewA
id objectA = [dataArrayA objectAtIndex:indexPath.row];
// configure the cell for tableViewA
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierA];
// etc...
} else {
// get the data for the current row in tableViewB
id objectB = [dataArrayB objectAtIndex:indexPath.row];
// configure the cell for tableViewB
cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifierB];
// etc...
}
return cell;
}
// And so on, the same idea applies for the other UITableViewDelegate you would need to
// implement...
- (void)dealloc {
[dataArrayA release];
[dataArrayB release];
[tableViewA release];
[tableViewB release];
[context release];
// etc...
[super dealloc];
}
#end
I hope you'll find this useful.

Storing Data help, cannot be placed into TableView

I have encountered a problem of placing data into the Table View. Here is my current code.
I am unable to load the results from the class I tried to use NSFetchedResultsController, but it won't work. Can any one see the mistake.
This is the header file.
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import <sqlite3.h>
#import "FlickrFetcher.h"
#interface PersonList : UITableViewController {
FlickrFetcher *fetcher;
NSArray *nameList;
NSArray *names;
NSFetchedResultsController *results;
NSArray *photos;
}
#property (retain, nonatomic)NSArray *nameList;
#property (retain, nonatomic)NSArray *names;
#property (retain, nonatomic)NSArray *photos;
#property (retain, nonatomic)NSFetchedResultsController *results;
#end
This is the .m file.
#import "PersonList.h"
#implementation PersonList
#synthesize nameList,names,results,photos;
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
self.title=#"Contacts";
// Custom initialization
}
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
fetcher = [FlickrFetcher sharedInstance];
NSString *path=[[NSBundle mainBundle]pathForResource:#"FakeData" ofType:#"plist"];
NSArray *array=[NSArray arrayWithContentsOfFile:path];
NSManagedObjectContext *context=[fetcher managedObjectContext];
if([fetcher databaseExists]==YES){
for(NSDictionary *dic in array){
PersonList *person=(PersonList *)[NSEntityDescription insertNewObjectForEntityForName:#"Person" inManagedObjectContext:context];
[person setNameList:[dic objectForKey:#"user"]];
[person setPhotos:[dic objectForKey:#"path"]];
names=[fetcher fetchManagedObjectsForEntity:#"Person" withPredicate:nil];
results=[fetcher fetchedResultsControllerForEntity:#"Person" withPredicate:nil];
}
}
[super viewDidLoad];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (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.names=nil;
}
- (void)dealloc {
[fetcher release];
[nameList release];
[super dealloc];
}
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[results sections] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"NOT REACHED HERE");
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 = [[results sections] objectAtIndex:row];
return cell;
}
#end
This the method for the FlickrFetcher method
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#interface FlickrFetcher : NSObject {
NSManagedObjectModel *managedObjectModel;
NSManagedObjectContext *managedObjectContext;
NSPersistentStoreCoordinator *persistentStoreCoordinator;
}
// Returns the 'singleton' instance of this class
+ (id)sharedInstance;
// Checks to see if any database exists on disk
- (BOOL)databaseExists;
// Returns the NSManagedObjectContext for inserting and fetching objects into the store
- (NSManagedObjectContext *)managedObjectContext;
// Returns an array of objects already in the database for the given Entity Name and Predicate
- (NSArray *)fetchManagedObjectsForEntity:(NSString*)entityName withPredicate:(NSPredicate*)predicate;
// Returns an NSFetchedResultsController for a given Entity Name and Predicate
- (NSFetchedResultsController *)fetchedResultsControllerForEntity:(NSString*)entityName withPredicate:(NSPredicate*)predicate;
#end
Put your call to [super viewDidLoad] before your code. The only time a call to super should be after your code is in the -dealloc method.
You are looping over array, getting a NSFetchedResultsController and doing nothing with it. You are not even keeping a reference. This is incorrect. You should have ONE NSFetchedResultsController per UITableViewController; you should retain reference to it; your UITableViewDatasource (which in this case is your UITableViewController) should be its delegate.
You are not calling -performFetch: on the NSFetchedResultsController so there is no data being retrieved.
Because you are not the delegate of the NSFetchedResultsController you would have no way of knowing when it got data back anyway because that is the singular way that it communicates data changes.
I would reconsider your design and review the Core Data iPhone sample projects again.
Update
The NSFetchedResultsController does not fire its delegate methods until you save the NSManagedObjectContext so that is why you are not seeing data.
As for the error you are getting, you are trying to set a property on an object that does not respond to that property. I would suggest loading your application into the debugger, putting a breakpoint on objc_exception_throw and see what object you are manipulating that is causing the problem. Most likely you are getting back one object and thinking it is another.

UITableView corruption upon scroll

Im trying to get a UITableView to display items contained within an array.
This array is contained within a class (HX_ParkingSearch).
I have a ref to this class that contains the array inside the app delegate class, to enable the views to access it.
Problem is that I get one page of results displaying correctly inside the tableview
but when i try and scroll down an exception occurs when trying to access the next item in the array.
It turns out that when i scroll down and the cellForRowAtIndexPath method fires,
the items inside the array are invalid and appear to have been released but i dont understand where
they are being released!
Does anyone have any ideas because this is really doing my head in now!
Many thanks,
Chris.
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
HX_ParkingLocation *location;
bookingApp2AppDelegate *del = (bookingApp2AppDelegate *) [[UIApplication sharedApplication] delegate];
NSMutableArray* array = [del.parkingSearch locations];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
location = (HX_ParkingLocation*) [array objectAtIndex: [indexPath row] ];
return cell;
}
#import <Foundation/Foundation.h>
#interface HX_ParkingLocation : NSObject
{
NSString *name;
}
#property(retain,nonatomic) NSString* name;
/*
Initialises this Location instance by passing in the name and code of the location and the URL of the webapi product endpoint.
The URL is used to find available products at this location.
*/
-(id) initWithName: (NSString*) n;
#end
#import <Foundation/Foundation.h>
#interface HX_ParkingSearch : NSObject
{
NSMutableArray* locations;
}
#property (retain) NSMutableArray* locations;
-(BOOL) loadLocations;
#end
#import "HX_ParkingSearch.h"
#import "HX_Parking_Location.h"
#implementation HX_ParkingSearch
#synthesize locations;
//Finds the locations
-(BOOL) loadLocations
{
[locations release];
//Create array to hold locations
locations = [[NSMutableArray alloc] initWithCapacity:30];
//Loop through all returned locations
for(int i=0;i<15;i++)
{
//Get location name
NSString* n = [NSString stringWithFormat:#"Item #%i",i ];
//Create location instance, which retrieves availability and product information and stores the information in location object.
HX_ParkingLocation* location = [[HX_ParkingLocation alloc] initWithName:n];
//add to array
[locations addObject:location];
}
return YES;
}
#end
#import <UIKit/UIKit.h>
#import "HX_ParkingSearch.h"
#interface bookingApp2AppDelegate : NSObject <UIApplicationDelegate> {
UIWindow *window;
UINavigationController *navigationController;
HX_ParkingSearch *parkingSearch;
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet UINavigationController *navigationController;
#property (retain) HX_ParkingSearch *parkingSearch;
#end
#implementation bookingApp2AppDelegate
#synthesize window;
#synthesize navigationController;
#synthesize parkingSearch;
- (void)applicationDidFinishLaunching:(UIApplication *)application
{
//Create new parking search instance by specifying the endpoint urls
HX_ParkingSearch* search = [[HX_ParkingSearch alloc] init];
[search loadLocations];
parkingSearch = search;
//NSLog(#"Search Retain count = %i" ,[search retainCount]);
[window addSubview:[navigationController view]];
//[window addSubview:[navigationController initWithNibName:#"VC_Locations" bundle:[NSBundle mainBundle]]];
[window makeKeyAndVisible];
}
Is there a reason that you're init-ing your array with a capacity of 30 in loadLocations, but only inserting 15 items?
what's in this method for your datasource?:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
and also:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
the tableview may just be trying to grab an index that's out of bounds of locations