EXC_BAD_ACCESS scrolling TableView - objective-c

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.

Related

How to NOT use dequeueReusableCellWithIdentifier?

I have a small table view, maybe 10 items max, and I do not want to use the dequeueReusableCellWithIdentifier method. The reason is, each time I'm scrolling the table view, I have the first row in place of the last row, which take few second to update back to get update back with the last row.
I'm using a prototype cell and i try to remove the :
JsonTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
but it return blank cells in table view.
So here is my code :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
JsonTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell=[[JsonTableViewCell alloc]initWithStyle:
UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
I also try to keep the
JsonTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
and configure the cell like:
if (cell == nil) {
cell.title.text = titleStrip;
}
but in this case, I've got the prototype cell as it is in the storyboard. It's not blank, but not filled with data.
Anybody can tell me what I am doing wrong?
UPDATE **** 10.10.2014 *******
Here is maybe the all code would help :
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return myObject.count;
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"Cell";
JsonTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell=[[JsonTableViewCell alloc]initWithStyle:
UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSDictionary *tmpDict = [myObject objectAtIndex:indexPath.row];
NSMutableString *text;
text = [NSMutableString stringWithFormat:#"%#",
[tmpDict objectForKeyedSubscript:title]];
NSMutableString *detail;
detail = [NSMutableString stringWithFormat:#"%# ",
[tmpDict objectForKey:content]];
NSURL *url = [NSURL URLWithString:[tmpDict objectForKey:thumbnail]];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *img = [[UIImage alloc]initWithData:data];
NSString *titleStrip = [[text description] stringByReplacingOccurrencesOfString:#""withString:#""];
NSString *detailStrip = [[detail description] stringByReplacingOccurrencesOfString:#""withString:#""];
dispatch_sync(dispatch_get_main_queue(), ^{
cell.titreNews.text = titleStrip;
cell.detailNews.textAlignment = NSTextAlignmentJustified;
cell.detailNews.text = detailStrip;
UIImage *image0 = [UIImage imageNamed:#"video.png"];
if (img != nil) {
cell.imageNews.image = img;
}
else {
cell.imageNews.image = image0;
}
});
});
[cell setAccessoryType:UITableViewCellAccessoryNone];
return cell;
}
This block of code is never called because you instantiate a cell before it (cell is never nil).
if (cell == nil) {
cell.title.text = titleStrip;
}
Don't call the dequeue methods if you aren't using them. Change your method to this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Don't even try to dequeue, just create new cell instance
JsonTableViewCell *cell = [[JsonTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
cell.title.text = titleStrip;
return cell;
}
First of all, welcome to Stack Overflow!
Second, I highly recommend against disabling dequeuing. Apple is famously strict when it comes to expected behaviors, and trying to re-work a basic UI element like a TableView to work in a different way could easily get your app rejected. Plus, dequeuing is a good thing, which helps keep memory usage down and streamlines things in general. It's far, far better to find a way to design your own code so it works with the dequeuing process as it is intended to work.
That being said, as I understand it all you need to do is omit the dequeueReusableCellWithIdentifier line, and it should work. cell will be nil each time, and will thus get re-initialized each time from scratch.

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];**
}];

iOS: Custom Cell lldb Memory access error

Still having trouble accessing variables to load into a custom cell. I have a xib, .h, .m named InSeasonCell. I have a IBOutlet InSeasonCell *_cell; in .h of rootviewcontroller. I get lldb error for accessing my productAtIndex values. Any help would be great. I was told to read the Table View Programming Guide.
I create
_dataController = [[InSeasonProductDataController alloc] init];
Which is init in the rootviewcontroller but passed to an other object which populates it with Product objects.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"InSeasonCell";
InSeasonCell *cell = (InSeasonCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
cell = _cell;
_cell = nil;
}
if(_dataController!=nil){
Product *productAtIndex = [_dataController objectInListAtIndex:indexPath.row];
// Configure the cell...
cell.name.text = productAtIndex.name;
cell.week.text = productAtIndex.week;
cell.image.image = productAtIndex.image;
}
return cell;
}
I looked through my code and found that I am passing an object created in one class to the other. for example:
-(id)initWithDataController:(InSeasonProductDataController *)dataController spinner: (UIActivityIndicatorView *)spinner{
if(self = [super init]){
_dataController = [dataController retain];
_spinner = [spinner retain];
}
return self;
}
_dataController gets populated with Product objects later in this method and is released.
To solve this error you need in your xib file click on cell prototype and in Attribute inspection > Identifier insert the same name as you have in .m file (I write down "cell"):
in method:
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
.......
Cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
Cell = [[MainCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
I figured it out. My Product object initialized variables which I had to do a copy for each variable passed in. This got rid of the lldb error.

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.

UITableView displays data then crashes

I'm making an iOS that lists the files in the Documents dictionary. I want to display this data in a UITableView, the problem is that it's not working.
It loads the data in to the view. Then the application freezes and calls EXC_BAD_ACCESSThis is my code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
timesRun = 0;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [paths objectAtIndex:0];
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectoryPath error:nil];
[bundleRoot release];
NSLog(#"Count: %i",[dirContents count]);
return [dirContents count];
}
- (UITableViewCell *)tableView:(UITableView *)tableViewData cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = nil;
int Locatation = indexPath.row;
Locatation++;
cell = [tableViewData dequeueReusableCellWithIdentifier:#"MyCells"];
if (cell == nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCell"]autorelease];
}
//cell.textLabel.text = [NSString stringWithFormat:#"Cell: %i",Locatation];
cell.textLabel.text = [dirContents objectAtIndex:timesRun];
timesRun++;
return cell;
NSLog(#"Did return");
}
- (void)tableView:(UITableView *)tableViewDat didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"%#",cell.textLabel.text);
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
This displays a basic misunderstanding of the concept of the table view data source.
My suggestion:
Create an array that contains the files outside any tableview methods. Then you use that array to feed the tableview.
Use array.count to return numberofrowsatindexpath.
Also, on providing the cells at cellforrowatindexpath, don't use iterations/counters. The tableview ask for each element of your array itself using the indexpath argument. You access it like this:
id object = [array objectArIndex: indexPath.row]
And you then use the objects attributes to set the labels of the cell.
Please read a tutorial on tableviews. I recommend itunesU Paul hegarty's lectures. They're really great.
Ps. You release the bundleRoot object in numberofrowsinsection, which you don't retain (or use at all) which is most likely causing your crash.
Edit:
With a little more spare time, I retouched your code:
//code not tested/written in xcode. Might contain typos
//in your .m file
//in your (private) interface
#property (nonatomic, retain, readonly) NSArray *dirContents;
//in your implementation
#synthesize dirContents=_dirContents;
-(NSArray*) dirContents {
if (!dirContents) {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryPath = [paths objectAtIndex:0];
_dirContents = [[[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectoryPath error:nil] retain];
}
return _dirContents;
//note that if you want to "refresh" the contents you would have to
//release _dirContents and set it to nil or implement this differently
}
-(void) dealloc {
[_dirContents release];
//....
[super dealloc];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [[self dirContents] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:#"MyCells"];
if (cell == nil) {
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"MyCells"]autorelease];
}
cell.textLabel.text = [[self dirContents] objectAtIndex: indexPath.row];
return cell;
//  NSLog(#"Did return"); //this never gets called by the way
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"%#",cell.textLabel.text);
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
My problem was sort of like Mario's answer. [dirContents count] returned a higher value than the amount of times it could add an object from the array.In other words it was a Out of Bounds error.
Make sure you are not releasing dirContents while using that.I am sure you are not doing but just cross check with your code.