Application (tableview) is crashing while scrolling - iphone

I'm making an app which tries to read the information from the .plist file (put there parsed JSON).
The reading from file flows nice: got the array of dictionaries, but while trying to display it on tableview, the problems start. The initial view is loaded properly, but when I start scrolling, the app crashes.
#define DOCUMENTS [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *filePathDocArray = [DOCUMENTS stringByAppendingPathComponent:#"filters.plist"];
NSString *filePathBundleArray = [[NSBundle mainBundle] pathForResource:#"filters" ofType:#"plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePathDocArray]) {
[[NSFileManager defaultManager] copyItemAtPath:filePathBundleArray toPath:filePathDocArray error:nil];
NSLog(#"File saved");
} else {
NSLog(#"File already exists");
filters = [NSArray arrayWithContentsOfFile:filePathDocArray];
}
}
Here I get all the info I need into filters array (checked by looping). Then:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [filters count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *myIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:myIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:myIdentifier];
}
NSInteger crow = indexPath.row;
NSDictionary *story = [filters objectAtIndex: crow];
cell.textLabel.text = [story objectForKey:#"Name"];
cell.detailTextLabel.text = [story objectForKey:#"Description"];
return cell;
}
#end
When the app starts everething is OK: I see the normel table view, but when I start scrolling it crashes
After series of breakpoint debugs I evaluated, that after the applications starts on Simulator, the link on array filters screws, so when I try to populate the next cell, the story dictionary can't be properly created.
What sort of problem it can be?
Here the console report:
2012-09-22 13:37:43.545 JSONExample[4559:207] -[__NSCFString
objectAtIndex:]: unrecognized selector sent to instance 0x6a083c0
2012-09-22 13:37:43.547 JSONExample[4559:207] *** Terminating app due
to uncaught exception 'NSInvalidArgumentException', reason:
'-[__NSCFString objectAtIndex:]: unrecognized selector sent to
instance 0x6a083c0'

are you using ARC??
if not then autorelease the cell first!!
and if filters is not a retain property.. kindly make it one and synthesize it and again if ARC not used then release it in the dealloc block.. that should do it i suppose..

-[NSCFString objectAtIndex:]: unrecognized selector seems to be the same problem.
Your 'filters' variable is an NSString/CFString at the time you call objectAtIndex: - not an array, as you would assume. The solution given in the question linked to is to retain your filters array whenever it's set.

If you wote property for the filters like #property (nonatomic, retain) NSArray *filters; you need to write like self.filters = [NSArray arrayWithContentsOfFile:filePathDocArray];
Else you need to write like filters = [[NSArray arrayWithContentsOfFile:filePathDocArray] retain];

You need to keep in mind another thing..
if you do this step
NSDictionary *story = [[filters objectAtIndex: crow]retain];
every time your cellForRowAtIndexPath is called the retain count would increase by 1. But you will delloc it only once.
So there will be memory leaks in your applications.
I suggest you go through the memory management guide once. its a small document. at max it will take one day of yours. But will feel more confident of what to do and what not to do.
http://developer.apple.com/library/ios/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.pdf
Cheers!! And Happy Coding!!

Related

UITableView crashes when scrolling

I've been attempting to create my own app for a band by my friends, and I've been experimenting with using a custom TableViewCell for news articles that appear on the website. My main objective is to get all of the data from the website, store it in a NSMutableArray, and then display that in my custom cells.
The app runs fine when it loads the first 5-6 cells. However, when I begin to scroll, the app crashes. I've pinpointed the in the cellForRowAtIndexPath: method. Using GDB, I've also come to find out that after I create my NSMutableArray data and once the program runs, after scrolling, my array seems to be autoreleased. I'm not sure why this happens. Here's what I have for my code thus far:
In HomeViewController.h:
#interface HomeViewController : UIViewController {
NSArray *results;
NSMutableArray *titles;
NSMutableArray *dates;
NSMutableArray *entries;
}
#property (nonatomic, retain) NSMutableArray *titles;
#property (nonatomic, retain) NSMutableArray *dates;
#property (nonatomic, retain) NSMutableArray *entries;
#end
In HomeViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
titles = [[NSMutableArray alloc] init];
dates = [[NSMutableArray alloc] init];
entries = [[NSMutableArray alloc] init];
while((i+1) != endIndex){
NSString *curr_title = [[NSString alloc] init];
NSString *curr_date = [[NSString alloc] init];
NSString *curr_entry = [[NSString alloc] init];
//do some character iterations across a string
[titles addObject:curr_title];
[dates addObject:curr_date];
[entries addObject:curr_entry];
[curr_title release];
[curr_date release];
[curr_entry release];
}
}
//more code here, removed
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"NewsCell";
NewsCell *cell = (NewsCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelObjects = [[NSBundle mainBundle]
loadNibNamed:#"NewsCell"
owner:self options:nil];
// cell = [topLevelObjects objectAtIndex:0];
for(id currentObject in topLevelObjects){
if([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (NewsCell *)currentObject;
break;
}
}
}
NSLog(#"%d", indexPath.row);
NSLog(#"%d", titles.count);
cell.cellTitle.text = [titles objectAtIndex:indexPath.row];
cell.datePosted.text = [dates objectAtIndex:indexPath.row];
cell.preview.text = [entries objectAtIndex:indexPath.row];
return cell;
}
Again, the first 5-6 cells show up. Once I scroll, I tried doing po titles and got this error:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00000009
0x011b309b in objc_msgSend ()
I've tried allocating the arrays in initWithNibName: but that didn't seem to do much. I've tried moving all the code in viewDidLoad: and then calling [super viewDidLoad] and it just produced the same results as well. Sorry this is so long, but I figured people needed to see my code.
I don't see anything obviously wrong with the code you posted. Try enabling the NSZombieEnabled environment variable. This prevents objects from being released so that when your application "crashes" you can determine which object caused the problem.
Also, instead of looping through array of objects returned by loadNibNamed:owner:options, you should assign the desired object to an IBOutlet property of your class. See Loading Custom Table-View Cells From Nib Files for an example.
The following is extracted from the new code you posted:
NSString *curr_title = [[NSString alloc] init];
//do some character iterations across a string
[titles addObject:curr_title];
[curr_title release];
NSString is not mutable (as is NSMutableString). Are you intentionally adding empty strings to the titles array?
In response to your comment: stringByAppendingString creates a new autoreleased NSString object.
NSString *curr_title = [[NSString alloc] init];
// this leaks the original NSString object (curr_title no longer points to it);
// curr_title now points to a new, autoreleased NSString
curr_title = [curr_title stringByAppendingString:#"..."];
[titles addObject:curr_title];
// releasing the autoreleased NSString will cause your application to crash!
[curr_title release];
*EXC_BAD_ACCESS* is a sure sign that one of your objects is getting over released (or wasn't retained properly). NSZombieEnabled is your friend here, just as titaniumdecoy suggests - figuring out what object is being over released is half the battle. Just be sure to turn it off before releasing the app, because (as titaniumdecoy pointed out) it prevents objects from getting released.
I usually use a combination of NSZombieEnabled and well placed breakpoints (so I can walk through the code till it crashes) to figure out where the problem is cropping up in the code. Then it's usually a simple matter of backtracking to figure out where the object was over released.
The problem might be with the implementation of NewsCell, all your properties there being retained?
Also, any reason HomeViewController is subclassing UIViewController and not UITableViewController?
And this should work just fine:
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"NewsCell" owner:self options:nil];
cell = [topLevelObjects objectAtIndex:0];

Objective-C/iOS : Message sent to deallocated instance

This is my first post on stackoverflow.com so please be kind (rewind) ;)
I have a navigation based application whose purpose is to display blog posts (title) in a Table View (with JSON).
The problem I ran into occurred when a cell got out of the screen and then back in.
I was getting a EXC_BAD_ACCESS (because I sent a message to a deallocated instance), so I struggled to understand where it came from and I finally found a solution. But the fact is I don't exactly understand how the problem occurs. That's why I need someone to enlighten me, I think this is fundamental understanding !
When the connection to the JSON web service has finished loading, I parse the JSON code to obtain a list of blog posts (recentPosts), then I create a BlogArticle object for each post (blogArticle), store it in a MutableArray iVar (allEntries) and insert a row in the Table View :
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSError *error;
SBJsonParser *json = [[SBJsonParser new] autorelease];
NSDictionary *recentPostsData = [json objectWithString:responseString error:&error];
[responseString release];
NSArray *recentPosts = [recentPostsData objectForKey:#"posts"];
int i = 0;
for (NSDictionary *post in recentPosts) {
BlogArticle *blogArticle = [[BlogArticle alloc] initWithDictionary:post];
[allEntries insertObject:blogArticle atIndex:i];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:i inSection:0]] withRowAnimation:UITableViewRowAnimationRight];
i++;
}
}
Here's the initialisation of the BlogArticle object which turned to be the origin of the problem :
- (id)initWithDictionary:(NSDictionary *)article
{
if (self = [super init])
{
// title = [[[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML] copy];
// title = [[NSString alloc] initWithString:[[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML]];
title = [[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML];
}
return self;
}
So every Objective-C programmer who isn't as noobish as me is able to tell that title is never allocated before being assigned. If I uncomment one of the two lines above it will work. The program crashes exactly when I try to initialize a cell with that title variable, here :
- (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];
}
NSLog(#"indexPath.row = %i", indexPath.row);
// Configure the cell.
BlogArticle *article = [allEntries objectAtIndex:indexPath.row];
cell.textLabel.text = article.title;
return cell;
}
Now, what I need to understand is why it does compile/work without allocating the iVar and where exactly it causes trouble (or where exactly the content of title is released causing the program to crash).
Any good resource (noob friendly) about memory management in iOS environment would be much appreciated.
Thanks in advance :)
This line
title = [[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML];
is allocating an autoreleased string. Essentially, think of it that an autoreleased string will get released at the end of the method (though it can last longer, it's useful to think of it that way).
You know the string is autoreleased because the name of the method gtm_stringByUnescapingFromHTML does not start with alloc, new, copy or mutableCopy.
You can add retain to this to stop it getting autoreleased:
title = [[[article valueForKey:#"title"] gtm_stringByUnescapingFromHTML] retain];
Now you own the string, and it will not get released until you say so.
The best summary I know of is Apple's own documentation here.
Well, the problem is, that you have to initialize your object, if you want to manage the memory of it on your own. Why should you manage now the memory of title?
Quite simple:
Every object reference, that is stored in an Array, Set, Dictionary etc. is managed by the Array, Dictionary and Set.
If you now just use this reference (by writing: "title = ...") in your cell, you will add the reference also to the cell. And now the cell is also responsible for the object-reference. So if the tableView wants to release your cells, which will happen from time to time to save memory, the cell will release your title-object. And this would cause the NSDitionary to be quite sad, since the NSDictionary wants to take care about the objects stored within itself.
So you could write the following in the tableView-method:
cell.textLabel.text = [article.title retain];
Or the commented lines of your own method.
That means, you will "raise" the storage-level of your object up and if it gets released, the storage level itself will be decreased by one.
If the storage-level will reach zero, it will be completely released (that should happen, if your tablecell is released AND your NSDIctionary)
I hope i could help you a bit :)

NSMutableArray removeObjectAtIndex:indexPath.row crashes

I have a UITableViewController with an NSMutableArray in the header like this:
NSMutableArray *someArray;
Also I have the property declared:
#property (nonatomic, retain) NSMutableArray *someArray;
In the .m file I load the array in the method:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
someArray = [[NSMutableArray alloc] init];
[self loadArrayData];
[tableview reloadData];
}
The table populates great, adding is no problem, but when I try to delete a row, the app crashes on the following line:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
[someArray removeObjectAtIndex:indexPath.row];
//update table etc..
If I use the debugger, I can see the array has some objects, and when I Log indexPath.row I get a value which is inside the array size.
I don't understand why it is crashing on this line... Who can help me?
The console outputs:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI removeObjectAtIndex:]: unrecognized selector sent to instance 0x6193890'
I had a very similar problem and I was initiating my array with CoreData like this:
self.allContacts = (NSMutableArray*)[cdc.managedObjectContext executeFetchRequest:fetchRequest error:&error];
trying to cast the result from CoreDataController fetch request to NSMutableArray.
Turns out I need the function called "mutableCopy" at the end of fetch request.
self.allContacts = (NSMutableArray*)[[cdc.managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
I don't think cast to (MutableArray *) is required but I have it just in case.
Hope this helps.
I had the same issue a week back. What I did was this:
Declared a NSMutableArray* globalArray as a global within the same tableViewController.m implementation file. Then inside
if (editingStyle == UITableViewCellEditingStyleDelete) {
globalArray = [[NSMutableArray alloc] initWithArray:someArray];
[globalArray removeObjectAtIndex:[indexPath row]];
}
And then I copied back this globalArray into someArray before the reloadData call. Works.
not sure if that cause one of your issues, but if you have property for someArray then use self.someArray instead of just someArray .
Of course that after using self, you should auto release it like this :
self.someArray = [[[NSMutableArray
alloc] init] autorelease];
(call this inside your viewController
init method)
then try to :
[self.someArray removeObjectAtIndex:indexPath.row];
2.
try to call [self loadArrayData]; inside viewDidLoad.
It should be called prior to cellForRowAtIndexPath, and that way you won't need to call reloadData
if that still won't help, then post the code of loadArrayData

How do I display data in a UITableView cell from an NSDictionary?

Hi i received 2000 of datas from udp and display the values in tableview .What is the easiest method to do this ?
Now i am using Two nsthreads and one thread for receive data via udp and stores it in NSMutableDictionary.Another thread update the tableview using these Dictionary values. But it crashes my app.
Here is some code i used
I stored Received values like this
NSMutableDictionary *dictItem
CustomItem *item = [[CustomItem alloc]init];
item.SNo =[NSString stringWithFormat:#"%d",SNo];
item.Time=CurrentTime;
[dictItem setObject:item forKey:[NSString stringWithFormat:#"%d",SNo]];
[item release];
Delegate method i used and i used CustomTableCells to display data as column vice.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [dictItem count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"CustomTableCell";
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil)
{
cell = [[[CustomTableCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
}
NSArray *keys = [dictItem allKeys];
CustomItem *item = [dictItem objectForKey:[keys objectAtIndex:indexPath.row]];
cell.SNo.text = item.SNo;
cell.Time.text = item.Time;
return cell;
}
The errror is
Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection was mutated while being enumerated.'
2010-07-23 02:33:07.891 Centrak[2034:207] Stack: (
42162256,
43320108,
42161198,
43372629,
41719877,
41719345,
9948,
3276988,
3237662,
3320232,
3288478,
71153942,
71153189,
71096786,
71096114,
71296742,
41650770,
41440069,
41437352,
51148957,
51149154,
2925426
)
terminate called after throwing an instance of 'NSException'
Can anyone help me ?
Thanks in advance.......
You probably have to use locking because when you access your dictionary from table view, it perhaps being mutated with other thread.
Try to look at NSLock docs. Before mutating your dictionary do [myLock lock]; and after mutating do [myLock unlock];. Similar in other thread: before enumerating dictionary do [myLock lock]; and after getting all values do [myLock unlock];.
myLock is a NSLock object and must be shared between your threads.
Mutable collections are not thread-safe by nature, so if you use them with multiple threads, you have to create an immutable copy first. For example, if you want to iterate through all the keys in your NSMutableDictionary, you would do this (assuming your NSMutableDictionary is called mutableDictionary):
NSDictionary *dictionary = [NSDictionary dictionaryWithDictionary:mutableDictionary];
for(id key in dictionary) {
// Do anything you want to be thread-safe here.
}
If you don't want to copy the dictionary, I suppose you could use locks or simply the #synchronized directive as follow:
#synchronized(mutableDictionary) {
// Do anything you want to be thread-safe here.
}
For more information, please have a look at the Apple documentation regarding multithreading and thread-safe objects: http://developer.apple.com/mac/library/documentation/cocoa/conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html

NSMutableArray removeObjectAtIndex: throws invalid argument exception

I'm writing an application to show some news from a portal. The news are fetched using a JSON file from the Internet and then stored into a NSMutableArray using the CoreData Model. Obviously a user can't delete the news from the JSON file on the Internet, but he can hide them locally. The problems come out here, where I have the following code:
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
if (editingStyle == UITableViewCellEditingStyleDelete) {
if( !moc ){
moc = [[NewsFetcher sharedInstance] managedObjectContext];
}
[[dataSet objectAtIndex:indexPath.row] setEliminata:[NSNumber numberWithBool:YES]];
NSError *error;
if( ![moc save:&error] ){
NSLog( #"C'รจ stato un errore!" );
}
[dataSet removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:YES];
}
The line:
[dataSet
removeObjectAtIndex:indexPath.row];
cause my apps to crash with the following error:
2010-07-12 19:08:16.021
ProvaVideo[284:207] * -[_PFArray
removeObjectAtIndex:]: unrecognized
selector sent to instance 0x451c820
2010-07-12 19:08:16.022
ProvaVideo[284:207] * Terminating
app due to uncaught exception
'NSInvalidArgumentException', reason:
'*** -[_PFArray removeObjectAtIndex:]:
unrecognized selector sent to instance
0x451c820'
I'm trying to understand why it doesn't work but I can't.
If I re-launch the app, the new is correctly logically cancelled.
Any suggestions?? Thanks in advance.
Interface:
#interface ListOfVideo : UITableViewController <NSFetchedResultsControllerDelegate> {
NSMutableArray *dataSet;
}
#property (nonatomic, retain) NSMutableArray *dataSet;
// In the .m file:
#synthesize dataSet;
Initialization in viewDidLoad:
dataSet = (NSMutableArray *) [[NewsFetcher sharedInstance]
fetchManagedObjectsForEntity:#"News"
withPredicate:predicate
withDescriptor:#"Titolo"];
[dataSet retain];
updateDatabase ... this is when checking for new news from the net, I add them in the MutableArray:
[dataSet addObject:theNews];
Your NewsFetcher returns you an immutable array, not a mutable instance. Use the following instead for initialization:
NSArray *results = [[NewsFetcher sharedInstance]
fetchManagedObjectsForEntity:#"News"
withPredicate:predicate
withDescriptor:#"Titolo"];
dataSet = [results mutableCopy];
An expression like A *a = (A*)b; only casts the pointer to a different type - it doesn't convert/change the actual type of the instance it points to.
Verify that dataSet is an NSMutableArray. The exception is getting thrown because it doesn't respond to removeObjectAtIndex.
jdot is correct dataSet must be NSMutableArray..
you must do it this way..
dataSet = [NSMutableArray arrayWithArray:[[NewsFetcher sharedInstance]
fetchManagedObjectsForEntity:#"News"
withPredicate:predicate
withDescriptor:#"Titolo"]];
Now the dataSet is a mutable instance of the array you got from NewsFetcher and it won't crash on removing objects.