Help with NSFetchedResultsController - objective-c

Please help with this issue of using NSFetchedResultsController.
I created an object of NSFetchedResultsController and I use it once in the method: tableView:cellForRowAtIndexPath: and when I try to execute the same code in the method tableView:didSelectRowAtIndexPath: I get EXC_BAD_ACCESS.
Here is the code of the 2 methods
- (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];
}
Person *person = [fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = person.name; //This works fine
[person release];
return cell;
}
and here is the problematic snippet:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
PhotoListViewController *photoListViewController = [[PhotoListViewController alloc] initWithNibName:#"PhotoListViewController" bundle:nil];
//The next line returns a bad object or undefined memory
Person *person = [fetchedResultsController objectAtIndexPath:indexPath];
//causing the call of [person name] to return EXC_BAD_ACCESS
photoListViewController.person = [person name];
[self.navigationController pushViewController:photoListViewController animated:YES];
[photoListViewController release];
[person release];
}
Please help me understand why the code is breaking there.
Appreciate any suggestions.

Person *person = [fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = person.name; //This works fine
[person release];
[person release] is the problem - no need to release person if it's not alloc'd there. That causes person to be over-released, leading to the crash when something else tries to access it.

Even though, in this example, it's ok to manipulate an autoreleased object (due to simplicity of the manipulations, i.e. you are just grabbing one property of *person), the proper way would be to retain your Person object and release it at the end:
Person *person = [[fetchedResultsController objectAtIndexPath:indexPath] retain];
// bla bla
[person release];

Related

Displaying data retrieved from Parse in UITableView

After all progress i made with your answers, my issue changed. So i am changing my question with clearer way. I have an UITableView which is showing my retrieved data from Parse.com. So i made a NSMutableArray for adding objects to that array when they are retrieved. But my problem is even i add objects to NSMutableArray, my table does not show anything but default screen of UITableView. I thing the issue is UITableView is formed before my NSMutableArray got its objects. Here is my code:
Note: The PropertyClass is the class which has the properties of my objects.
At MyTableViewController.h
#interface MyTableViewController : UITableViewController <CLLocationManagerDelegate> {
PFObject *object;
}
#property (strong, nonatomic) IBOutlet UITableView *MyTableView;
#end
At UITableViewController.m
#interface MyTableViewController ()
#property(strong)NSMutableArray *myNSMutableArray;
#end
#implementation MyTableViewController
#synthesize myNSMutableArray,MyTableView;
-(void) retrievingDataFromParse{
PFQuery *query = [PFQuery queryWithClassName:#"MyObjectsClass"];
[query whereKey:#"ObjectsNumber" lessThanOrEqualTo:10];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Successfully retrieved %d scores.", objects.count);
if (objects.count==0) {
NSString *objectError = #"There no object retrieved from Parse";
PropertiesClass *PC = [[PropertiesClass alloc]initWithPropert1:objectError Propert2:nil Propert3:nil Propert4:nil];
[myNSMutableArray addObject:PC];
}
for (int i = 0; i < objects.count; i++) {
object = [objects objectAtIndex:i];
NSString *Propert1 = [object objectForKey:#"Propert1"];
NSNumber *Propert2 = [object objectForKey:#"Propert2"];
NSNumber *Propert3 = [object objectForKey:#"Propert3"];
NSString *Propert4 = [object objectForKey:#"Propert4"];
PropertiesClass *PC = [[PropertiesClass alloc]initWithPropert1:Propert1 Propert2:Propert2 Propert3:Propert3 Propert4:Propert4];
[myNSMutableArray addObject:PC];
};
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.myNSMutableArray = [NSMutableArray array];
[self retrievingDataFromParse];
[MyTableView reloadData];
}
- (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 [myNSMutableArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PropertiesClass *PC= [myNSMutableArray objectAtIndex:indexPath.row];
cell.textLabel.text=PC.Propert1;
return cell;
}
Looking at your code i see that you never create a UITableViewCell, you should change this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
PropertyClass *PC = [myMutableArray objectAtIndex:indexPath.row];
cell.textLabel.text = PC.x;
return cell;
}
with this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (nil == cell){
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
PropertyClass *PC = [myMutableArray objectAtIndex:indexPath.row];
cell.textLabel.text = PC.x;
return cell;
}
the method dequeueReusableCellWithIdentifier:forIndexPath: return a UITableViewCell only if there are unused, but already allocated, cells in your table view. otherwise it returns nil.
Also when you update the mutable array containing all your data you should call [yourTableView reloadData] to force the table view to reload its content.
Your code is quite cryptic. Few suggestions here.
First, rename variables and methods with camelCaseNotation (camel case notation). For example, MyMutableArray should be myMutableArray. RetrievingDataFromParse should be retrievingDataFromParse (and so on). Start upper case letter are for classes.
Second, what does this code mean (I put comment on your code)?
for (int i = 0; i < objects.count; i++) {
// where do you have defined object?
object = [objects objectAtIndex:i];
NSString *x = [object objectForKey:#"x"];
NSNumber *y = [object objectForKey:#"y"];
NSNumber *z = [object objectForKey:#"z"];
NSString *t = [object objectForKey:#"t"];
// is Mekan a subclass of PropertiyClass or what else?
PropertiyClass *Properties = [[Mekan alloc]initWithx:x y:y z:z t:t]
// what's MekanKalibi? Maybe you need to add Properties
[MyMutableArray addObject:MekanKalibi];
}
Edit
If you don't use iOS6 - (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier you should alloc-init cells.
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if(!cell) {
// alloc-init a new cell here...
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
// or if you don't use ARC
// cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
PropertyClass *PC = [myMutableArray objectAtIndex:indexPath.row];
cell.textLabel.text = PC.x;
return cell;
Edit 2
I don't know how parse works but I suppose it manages async requests. So, at the end of your for loop, just call reload data in the table.
Parse states:
The InBackground methods are asynchronous, so any code after this will run immediately. Any code that depends on the query result should be moved inside the completion block above.
I had the same problem. When you reload the table, you need to move it so it is inside the block. Worked for me.
I'm not 100% sure how the asynchronous parts affect it so. I know that the start of my viewDidload and the end occured then this block, hence the problem.
People should probably up this as this solves the issue.
Cheers.
All you have to do is reload tableView in the block... this will show data.
for (int i = 0; i < objects.count; i++) {
object = [objects objectAtIndex:i];
NSString *Propert1 = [object objectForKey:#"Propert1"];
NSNumber *Propert2 = [object objectForKey:#"Propert2"];
NSNumber *Propert3 = [object objectForKey:#"Propert3"];
NSString *Propert4 = [object objectForKey:#"Propert4"];
PropertiesClass *PC = [[PropertiesClass alloc]initWithPropert1:Propert1 Propert2:Propert2 Propert3:Propert3 Propert4:Propert4];
[myNSMutableArray addObject:PC];
};
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
**[MyTableView reloadData];**
}];

EXC_BAD_ACCESS scrolling TableView

im getting EXC_BAD_ACCESS when i scroll my TableView. I heard something like alloc being called wrong, I dont know. Here's my code:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[resultsDictionary objectForKey: #"bills"] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Identifier for retrieving reusable cells.
static NSString *cellIdentifier = #"MyCellIdentifier";
// Attempt to request the reusable cell.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// No cell available - create one.
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:cellIdentifier];
}
NSArray *billsArray = [resultsDictionary objectForKey:#"bills"];
cell.textLabel.text = [NSString stringWithFormat:#"%#", [[billsArray objectAtIndex:indexPath.row] objectForKey:#"name"]];
return cell;
}
EDIT
I think the error is here:
* -[JKArray objectAtIndex:]: message sent to deallocated instance 0x6a5d030
NSString *cellName = [NSString stringWithFormat:#"%#", [[billsArray objectAtIndex:indexPath.row] objectForKey:#"name"]];
It looks like resultsDictionary is a dangling pointer. If you are using ARC, you need a strong reference to it somewhere. If you are not using ARC, you need to retain it somewhere.
Fixed. My billsArray was missing 'self' instance, as inside of cellForRowAtIndexPath method. Thanks to everyone.

NSArray elements which have from NSSet not displayed in TableViewCell

I have NSArray elements which is implemented from NSSet and if i tried to display the elements in Table View Cell i'm getting BAD ACCESS issue at tableView numberOfRowsInSection part.Here is my code
- (void)viewDidLoad
{
[super viewDidLoad];
jsonurl=[NSURL URLWithString:#"http://www.sample.net/products.php"];//NSURL
jsondata=[[NSString alloc]initWithContentsOfURL:jsonurl];//NSString
jsonarray=[[NSMutableArray alloc]init];//NSMutableArray
self.jsonarray=[jsondata JSONValue];
array=[jsonarray valueForKey:#"post_title"];
set = [NSSet setWithArray:array];//NSMutableSet
array=[set allObjects];//NSArray
NSLog(#"%#",array);
}
#pragma mark - Table view data source
- (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
{
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.text = [self.array objectAtIndex: [indexPath row]];
return cell;
}
Kindly help please.Thanks in advance.
In your code you are not allocating the array. You are setting an autoreleased object to that array that's why you are getting this error.
Replace array=[set allObjects]; with array=[[set allObjects] retain];
I think this is because you are setting your instance variables to autoreleased objects without retaining them.
Either make "set" and "array" retained properties and do
self.set = [NSSet setWithArray:self.array];
// This is already a bit weird... If the set is made from the array, the array will be unchanged.
self.array = [self.set allObjects];
Or just retain them:
set = [[NSSet setWithArray:array] retain];
etc.
Since setWithArray and allObjects return autoreleased objects, you are left with dangling pointers as soon as you leave the scope of viewDidLoad.

Instruments/ Leaks show [_NSCoreManagedObjectID allocateBatch:Count:]

I am getting a crash when i go back and forth in navigation controller where one has a tableview. I used instruments to see what is going on and i see that when i go back to the first view controller i get
[_NSCoreManagedObjectID allocateBatch:Count:] leaks. Here is the screenshot from Instruments
The more i go back and forth the more leaks come up. What would be the reason for this? I set nsfetchedresultcontroller to nil in viewdidunload, but i don't set it to nil when i go between tabs.
Thanks!
UPDATE:
I did some testing and i found that if i comment
Person *person = [self.fetchedResultsController objectAtIndexPath:indexPath];
in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"personCell";
PeopleListTableViewCell *cell = (PeopleListTableViewCell *) [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[PeopleListTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell...
Person *person = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.personName.text = person.fullname;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MM-dd-yyyy"];
NSDate *tmpDate = person.dateofbirth;
NSString *strDate = [dateFormatter stringFromDate:tmpDate];
cell.personDateOfBirth.text = strDate;
NSString *imgURL=[person.imageurl stringByAppendingString:#"?maxheight=120&maxwidth=156"];
[cell.personImage setImageWithURL:[NSURL URLWithString:imgURL] placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
return cell;
}
The leak goes away. I am using this tableviewcontroller as a delegate to a tableview in a different uivewcontroller.
This is how i set it when the button is clicked to show this table
- (void)signPeople
{
self.signInfoView.hidden = YES;
self.pplTableView.hidden = NO;
self.hud = [[ATMHud alloc]initWithDelegate:self];
[self.hud setActivity:YES];
self.hud.shadowEnabled = YES;
[self.view addSubview:self.hud.view];
[self.hud show];
if(!self.pplListTableViewController){
self.pplListTableViewController = [[PeopleListTableViewController alloc]init];
}
self.pplListTableViewController.delegate = self;
self.pplTableView.delegate = self.pplListTableViewController;
self.pplTableView.dataSource = self.pplListTableViewController;
//shows the People List
[self.pplListTableViewController setupFetchResultsController];
[self.pplTableView reloadData];
[self.pplTableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES]; //scroll to top
}
I tried to nil out the tableview here and there and also dealloc the labels and person class nothing helps. What could be keeping the people class from getting freed. thanks!

Why am I getting a message sent to deallocated instance error in this case?

I am trying to sort a NSMutableArray based on an object it contains. I am getting an error on this line in the code segment below:
InboxItem * ptrInboxItem = [sortedInboxFaxItems objectAtIndex:[indexPath row]];
#import <UIKit/UIKit.h>
#class InboxItem;
#interface InboxTableViewController : UITableViewController<NSXMLParserDelegate> {
NSMutableArray *inboxFaxItems;
NSArray * sortedInboxFaxItems;
InboxItem *_inboxItem;
NSMutableData *xmlData;
NSURLConnection *connectionInprogress;
NSMutableString *inboxFaxesString;
UIActivityIndicatorView *activityIndicator;
}
#property(nonatomic,retain) InboxItem * inboxItem;
-(void) loadInbox;
#end
- (void) connectionDidFinishLoading:(NSURLConnection *)connection{
NSXMLParser *parser = [[NSXMLParser alloc]initWithData:xmlData];
[parser setDelegate:self];
[parser parse];
[parser release];
//lets sort by messageID
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"messageID" ascending:YES] autorelease];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
sortedInboxFaxItems = [inboxFaxItems sortedArrayUsingDescriptors:sortDescriptors];
[[self tableView] reloadData];
activityIndicator.stopAnimating;
[connectionInprogress release];
connectionInprogress = nil;
[xmlData release];
xmlData = nil;
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"InboxFaxItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
//I AM GETTING ERROR HERE
InboxItem * ptrInboxItem = [sortedInboxFaxItems objectAtIndex:[indexPath row]];
[[cell textLabel]setText: ptrInboxItem.datetime];
cell.imageView.image = [UIImage imageNamed:#"document.png"];
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.
MyManager *sharedManager = [MyManager sharedManager];
InboxItem * ptrInboxItem = [sortedInboxFaxItems objectAtIndex:[indexPath row]];
sharedManager.pages = ptrInboxItem.pages;
sharedManager.from =ptrInboxItem.from;
FaxViewController *faxViewController = [[FaxViewController alloc] initWithNibName:#"FaxViewController" bundle:nil];
faxViewController.messageid=ptrInboxItem.messageID;
faxViewController.navigationItem.title=#"View Fax";
[self.navigationController pushViewController:faxViewController animated:YES];
[faxViewController release];
}
It's because of this line:
sortedInboxFaxItems = [inboxFaxItems sortedArrayUsingDescriptors:sortDescriptors];
You're assigning an object you don't own to an instance variable. Later, when you try to access it, that object has been deallocated, so your instance variable now points to garbage.
You should change that line to this:
[sortedInboxFaxItems release]; // Release any previous value
sortedInboxFaxItems = [[inboxFaxItems sortedArrayUsingDescriptors:sortDescriptors] retain];
All better.