Objective C - UITableViewCell crashing on scroll - objective-c

I have a UITableViewCell with a method like this.
-(void) setupStore:(StoreModel *) store {
self.title.text = store.title; // crash here when scrolling
}
So that method is called from within a UIViewController class that contains the UITableView.
Something like this
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
[cell setupStore:[storesArray objectAtIndex:indexPath.row]];
...
}
That works when the table first loaded, but when I scroll the table, it crash with error EXC_BAD_ACCESS.
What could be causing that?
Please enlight.
Thanks,
Tee

Try to build your code with NSZombieEnabled = YES and report here what is happening. Give us the full error description.
http://cocoa-nut.de/?p=16

In general We will get EXC_BAD_ACCESS when we are trying to use a released object.
So you can check whether you are using any released object.
As you have mentioned that storesArray = [[[storesLocation alloc] init]retain]; there is no need to retain the object. Give a try by using this line
storesArray = [[storesLocation alloc] init];.
Also make sure that storemodel object exists by logging it in this method
-(void) setupStore:(StoreModel *) store
{
NSLog(#"store model %#",store);
}
You can go through the link
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html

Related

Why use Message Forwarding

Why would you use Message Forwarding in Objective-c. Also when using it, you need to call the Surragate method with performSelector? I am thinking you have a lot of knowledge when you are coding this, why would you need Message Forwarding. I am reading that this is Obj-c multiple inheritance - but I don't see it like this, it is just another object attached to an object.
Hope you know and have a pratical example
Thanks
See my discussion at the end of this section: http://www.apeth.com/iOSBook/ch25.html#_uitabbar
Here I have interposed my own object as the data source for one of Apple's table views. I don't know how the table view works (it's Apple's) and I don't know what their data source does (it's Apple's). So I substitute my own data source but I keep Apple's, and any messages that I don't handle that arrive at my data source are passed on to Apple's. Thus in a sense I have made my data source "inherit" from Apple's.
#implementation MyDataSource
- (id)forwardingTargetForSelector:(SEL)aSelector {
if ([self.originalDataSource respondsToSelector: aSelector])
return self.originalDataSource;
return [super forwardingTargetForSelector:aSelector];
}
- (NSInteger)tableView:(UITableView *)tv numberOfRowsInSection:(NSInteger)sec {
// this is just to quiet the compiler
return [self.originalDataSource tableView:tv numberOfRowsInSection:sec];
}
- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)ip {
// this is why we are here: my tweaks
UITableViewCell* cell = [self.originalDataSource tableView:tv cellForRowAtIndexPath:ip];
cell.textLabel.font = [UIFont fontWithName:#"GillSans-Bold" size:14];
return cell;
}
#end

UITableViewDataSource and Multithreading

I'm running into index beyond bounds exception in one of my UITableViews and I think it could be down to some multithreading issues. Here's what I believe is happening:
I have a UITableView and it's data source is a regular NSMutableArray.
This NSMutableArray which is backing my UITableView is updated every couple of seconds with the contents of an API response.
After each update, UITableView's reloadData is being invoked to ensure that the user sees new data from the API server.
Sometimes a index beyonds bounds exception gets thrown.
Here's my code:
-(NSMutableArray*) currentBetEvents
{
return currentMarketId == nil ? [[BFOpenBetsModel sharedInstance] betEvents] : filteredBetEvents;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
NSArray *betEvents = [self currentBetEvents];
return [betEvents count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSArray *betEvents = [self currentBetEvents];
id obj = [betEvents objectAtIndex:indexPath.section] // this is where it blows up
Basically, I get an exception while trying to access an object in the betEvents structure at index 0.
What I believe is happening is:
reloadData is called on the UITableView
numberOfSectionsInTableView: is invoked which returns a value > 0.
a rouge thread arrives and clears out the UITableView's data source.
cellForRowAtIndexPath: is invoked and it bombs.
Is there any way to ensure that this doesn't happen? Do I need to start using some primitive locks on the data source to ensure that it doesn't get updated while the table is being updated?
EDIT
Took another look at how the data structures returned by currentBetEvents can be altered and it looks like the filteredBets & betEvents can be cleared out as a result of the following code:
[[NSNotificationCenter defaultCenter] postNotificationName:kUserLoggedOutNotification object:nil];
This notification is posted whenever the user logs out. Whenever a user logs out of the app, I need to clear out the filteredBets and betEvents arrays. Is it possible that the following could happen:
reloadData is called on the UITableView
numberOfSectionsInTableView: is invoked which returns a value > 0.
User logs out which kicks off the notification & clears out the data structures.
cellForRowAtIndexPath: is invoked and it bombs.
Thanks,
Sean
Definitely sounds like a threading problem. You might try something like this:
// view controller
#synchronized([[BFOpenBetsModel sharedInstance] betEvents])
{
[self.tableView reloadData];
}
…
// data model
#synchronized(_betEvents) // or whatever the instance variable -betEvents returns is
{
[_betEvents addObject:whatever];
}

Loading a UITableViewCell with a NSOperationQueue

Hi guys I have an UITableView that loads precomputed cells from NSMutableArray. I want to use NSOperationQueue or PerformSelectorOnMainThread to update the user interface to enable smooth scrolling but i get an error... this is my code...
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//queue is being initialized in viewDidLoad
[queue addOperationWithBlock:^ {
[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
NSLog(#"Updating...");
return [self.CellObjects objectAtIndex:indexPath.row];
//if you remove the line above with the return, NSOperationQueue will work but I need the above line to load the cell.
}];
}];
}
Is there a way to make it work? Any help appreciated!
Why not simple ...
return [self.CellObjects objectAtIndex:indexPath.row];
...?
It's a mess. Why do you have two calls to addOperationWithBlock:? And also your return statement has nothing to do with return value of tableView:cellForRowAtIndexPath:. It's return value of your block, so, it will never work.
What's your error? I assume it's about incompatible block pointer, because it expects void(^)(void) and you're trying to send UITableViewCell *(^)(void).
Blocks are not gonna help you there. If you have precomputed cells in CellObjects, just use only the return self.CellObjects[indexPath.row]; line.
Also don't use property names like CellObjects. Should be named cellObjects. Check the case.
Offsetting your cell retrieval in that way is not going to give you any advantage. You need to compute your cell height/size/content prior to your UITableView instance asking for cells.
UITableView is expecting a UITableViewCell to be returned from that delegate callback on the
main thread.
A better idea would be to place the computation on other threads if they require time and their on completion you can call back to your UITableView to reloadData.

Can't get contents of file from a .plist file into an array

I'm trying to create a create a simple app where I have a TableView that should display the contents of a file. I have created a Table View in IB and dragged it's delegate and data source to the file's owner and I have manually created a .plist file with 1 array that have 2 items.
In my TableViewController.h i have declared an array.
NSArray * posts;
In my implementation file I have declared the required methods for UITableViewDataSource like this:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"Returning num sections");
return posts.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// create a cell
UITableViewCell * post = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"post"];
// fill it with content
post.textLabel.text = [posts objectAtIndex:indexPath.row];
// return it
return post;
}
And in my ViewController 'viewDidLoad' method I try to add the content of my file to the 'posts' array like this:
- (void)viewDidLoad
{
NSString * postFile = [[NSBundle mainBundle] pathForResource:#"Posts" ofType:#"plist"];
posts = [[NSArray alloc] initWithContentsOfFile:postFile];
NSLog(#"%#", postFile);
NSLog(#"%i", posts.count);
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
NSLog(#"%i", posts.count); returns 0, despite that I have added values to my .plist file. And nothing is displayed in the table view.
Suggestions on how so solve this would be appreciated.
I think you need to reload your table after you've loaded your postFile NSArray. If your view controller is a UITableViewController, try adding the following line of code to the end of your viewDidLoad method:
[self.tableView reloadData]
(On an unrelated note, you should also make your call to the super class the first thing you do in the viewDidLoad method, hence the comment the xcode template gives you.)
Edit: Problem with count.
I think you also have a problem with your debugging. count isn't a property of NSArray, so you can't use the dot syntax with it. You should be sending a message to your NSArray instance i.e. [posts count].
Ok, it seems like Xcode 4 creates plists with Dictionary as it's root type. If you want to use an array you have to open the .plist file in another text editor (probably doable in Xcode too) and change < dict >< /dict /> to < array >.
Also, it wasn't necessary to use an array at all. This also worked:
// Changed my array to a dictionary.
NSDictionary * posts;
// Get the cell text.
NSString * cellText = [[NSString alloc] initWithFormat:#"%i", indexPath.row];
// fill it with content
post.textLabel.text = [posts valueForKey:cellText];

warning received at objectsAtIndexes

I am trying to fetch data from core data, it works fine. But when I try to fill a Table Cell then it gives me warning at the following line
cell.textLabel.text = [fetchedObjects objectAtIndex:indexPath.row];
Warning : passing argument 1 of 'objectsAtIndexes:' makes pointer from integer without a cast.
fetchedObjects is an NSArray object declared in header file. Kindly guide me where I am doing wrong. I can provide whole method for more understanding.
Regards.
EDIT
Here is my complete method code:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"] autorelease];
cell.textLabel.text = [fetchedObjects objectAtIndex:indexPath.row];
return cell;
}
Your posted code says you're calling objectAtIndex, which does indeed take an integer parameter. However, your error says you're using objectAtIndexes, which takes a pointer to an NSIndexSet. As indexPath.row is not an NSIndexSet pointer, you're getting that warning (saying that you're trying to use a raw integer as a pointer).
Check your actual code. Almost certainly, you're using objectAtIndexes by mistake.