UITableView crashes when scrolling - iphone

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

Related

Application (tableview) is crashing while scrolling

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!!

I can not access my variable in EasyTableView delegate method

I use "EasyTableView" which is an extend UITableView. I encounter a problem as below:
- (void)viewDidLoad
{
NSArray *array = [[NSArray alloc] initWithObjects:#"14.jpg",nil];
photos = array;
[array release];
[photos objectAtIndex:0]; //this "photos" can be access
}
but when i try to access the "photos" in the delegate method:
- (void)easyTableView:(EasyTableView *)easyTableView setDataForView:(UIView *)view forIndexPath:(NSIndexPath *)indexPath {
NSInteger r = [indexPath row]; // r = 0
NSString *imagePath = [photos objectAtIndex:r]; //this line cause : reason: '-[CALayer objectAtIndex:]: unrecognized selector sent to instance
}
Why the result to access photos is different? How can i fix it?
use photos as property as
#property (nonatomic, retain) NSMutableArray* photos;
and in implementation
#synthesize photos = photos;
it will be accessible in delegate now
The photos variable isn't retaining the array you assign to it. When it is deallocated, the memory is being re-used to point to a CALayer object. If photos is a retained property, you should use self.photos = array; to make the assignment. If it's just a simple variable, then allocate it directly instead of using array.

NSDictionary to NSArray, will not display properly?

I have a signature page, were the user can sign their name.
It needs to then be saved to NSDictionary, but i want to call a List of the keys to be text in a TableView for each row or cell.
so:
"viewImage = saved as object to key:Random Number"
That parts somewhat easy, the hard part is when i call it on the other Page to the TableView.
It Exits the App with Error"SIGABRT". Now all my Delegates are in place and working...i believe.
now heres some example code:
FirstPage.m
UIImagePNGRepresentation(viewImage);
NSMutableArray *innerArray = [[NSMutableArray array]init];
[innerArray addObject:viewImage];
[SignatureSave setObject:innerArray forKey:#"5599"];
simple Enough, but doesnt give me an error.
SecondPage.m
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
FirstPage *appShare = (FirstPage *)[[UIApplication sharedApplication] delegate];
NSArray *dataDuplicate = [[NSArray alloc]init ];
dataDuplicate = [appShare.SignatureSave allKeysForObject:#"innerArray"];
static NSString *CellIdentifier = #"Cell";
NSLog(#"%#",dataDuplicate);
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil)
{
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero]autorelease];
}
if (dataDuplicate != nil) {
cell.textLabel.text = [dataDuplicate objectAtIndex:indexPath.row];
}
else
{
UIAlertView *CellAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Error Loading content, Try Again Later." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[CellAlert show];
[CellAlert release];
}
return cell;
}
#end
Now, How do i get the viewImage to save to the NSDictionary, to be able to call it on the SecondPage and display the name of the objects in the TableVIew?
I really don't understand whats your problem exactly.
first of all, is your dictionary a retained object?
//FirstPage.h
#property (nonatomic, retain) NSDictionary *SignatureSave;
//FirstPage.m
#synthesize SignatureSave;
…
UIImagePNGRepresentation(viewImage);
NSMutableArray *innerArray = [NSMutableArray array]; // using "array" is equivalent to alloc-init-autorelease
[innerArray addObject:viewImage];
[self.SignatureSave setObject:innerArray forKey:#"5599"];
// OR setting the array directly:
UIImagePNGRepresentation(viewImage);
NSArray *innerArray = [NSArray arrayWithObject:viewImage];
[self.SignatureSave setObject:innerArray forKey:#"5599"];
// OR even setting the image directly to the dictionary:
UIImagePNGRepresentation(viewImage);
[self.SignatureSave setObject:viewImage forKey:#"5599"];
now if you access the object by writing self. in front it will call the retain and your object will stay alive. Otherwise it would be autoreleased at the end of the method. This will fix the problem that your dictionary is maybe not present/available at table view creation and you don't have to use a singleton.
what are you trying to access with this code?
FirstPage *appShare = (FirstPage *)[[UIApplication sharedApplication] delegate];
with [[UIApplication sharedApplication] delegate] you get your application delegate (obviously). These are the MyAppNameAppDelegate files but you treat it as a FirstPage class.
Just NSLog() to check you get the right class, the one you expect.
NSLog(#"%#", [[[UIApplication sharedApplication] delegate] class]);
here you have a potential leak, you alloc-init but never release it:
NSArray *dataDuplicate = [[NSArray alloc]init ];
dataDuplicate = [appShare.SignatureSave allKeysForObject:#"innerArray"];
furthermore you can simplify it (will be autoreleased):
NSArray *dataDuplicate = [appShare.SignatureSave allKeysForObject:#"innerArray"];
and here you have another issue.
Why do you call all keys for the object #"innerArray"?
you don't have such an object and it's in many more cases wrong. innerArray was your previously named array in FirstPage.m but it is only for you as a developer to remember the variable better. After compilation it will have a cryptic name anyway. You could access your key #"5599" if you like but I don't think you want this. In your case you want to access all keys of the dictionary so simply call:
NSArray *dataDuplicate = [appShare.SignatureSave allKeys];
now you will have an array with all keys of your dictionary and you can access them like you do with objectAtIndex:.
NSString *keyName = [dataDuplicate objectAtIndex:indexPath.row];
cell.textLabel.text = keyName;
id theObject = [appShare.SignatureSave objectForKey:keyName]; // for example the image
Tell me if this solves your problems or tell me how I misunderstood your question.
I found the answer to this to be quit simple actually,
I ended up going with the Singleton Method instead of the Global Variable Method.
Now the Singleton Method looks terrifying but its quit simple, See here.
The main difference i noticed from the singleton method to the global method is,
Global method takes a lot of converting and re-converting.
Though the Singleton Method is working with a single object over many pages or classes.
Now i hope this will better assist people in the future also!

Iphone: threading issue?

I am initialing a dictionary in viewDidLoad and using it to create table view cells.
The first load works fine. But as soon as I scroll the table view to see the item (not displayed at the bottom) the app crashes. Through the Debugger I noticed the address of the dictionary item "rootItemsDict" changes when I did the scroll. Not able to figure out why that is. From my understanding the address of an object initialized once should remain same, at least within the given class instance. Any thoughts?
- (void)viewDidLoad {
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"menu" ofType:#"plist"];
rootItemsDict = [NSDictionary dictionaryWithContentsOfFile:path];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *title = (NSString *)[[[rootItemsDict objectForKey:#"rootMenuItems"] objectAtIndex:row] objectForKey:#"heading"];
+dictionaryWithContentsOfFile: returns an autoreleased instance. To take ownership you need to explicitly retain it:
rootItemsDict = [[NSDictionary dictionaryWithContentsOfFile:path] retain];
... or use the alloc/init form:
rootItemsDict = [[NSDictionary alloc] initWithContentsOfFile:path];
... or if you have a suitable property declaration (retain) use the setter:
self.rootItemsDict = [NSDictionary dictionaryWithContentsOfFile:path];
I recommend to read the Memory Management Programming Guide, especially the section on object ownership.

object crashes Application

i got an NSArray which gets filled in the init Method of my UITableViewController.
i use this object in "didSelectRowAtIndexPath" for pushing another tableviewcontroller.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ablogSingleCatTableViewController *singleCatTableViewController = [[ablogSingleCatTableViewController alloc] initWithStyle:UITableViewStylePlain category:[categories objectAtIndex:indexPath.row]];
[[self navigationController] pushViewController:singleCatTableViewController animated:YES];
[singleCatTableViewController release];
}
this works a few times when i start my application. after selecting a row and getting back to the main uitableview controller at a rondom point my application crashes after selecting a row.
with some nslogs i found out, that it crashes if i try to use my "categories" object.
so
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"before");
NSLog(#"cats: %#", categories);
NSLog(#"after");
ablogSingleCatTableViewController *singleCatTableViewController = [[ablogSingleCatTableViewController alloc] initWithStyle:UITableViewStylePlain category:[categories objectAtIndex:indexPath.row]];
[[self navigationController] pushViewController:singleCatTableViewController animated:YES];
[singleCatTableViewController release];
}
with that code my application crashes after "before" ... "after" never shows up.
i dont know why my "categories" object is crashing my application ?!
my categories object is defined in my header file and has a #property (nonatomic, retain). i synthesize it and releasing it in my dealloc method.
anyone has an idea?
// edit:
some more details here, because of the comments:
Debugger Console says: "Program received signal: “EXC_BAD_ACCESS”.
i create the category array like this:
- (void)initCategories {
NSString *path = [[NSBundle mainBundle] pathForResource:#"Categories" ofType:#"plist"];
[self setCategories:[[NSArray alloc] initWithContentsOfFile:path]];
}
calling this method in my initwithstyle method
[self initCategories];
my other custom initializing method looks something like this:
- (id)initWithStyle:(UITableViewStyle)style category:(NSDictionary*)cat {
if (self = [super initWithStyle:style]) {
currentCategory = cat;
items = [[NSMutableArray alloc] init];
self.title = [currentCategory objectForKey:#"TITLE"];
//XLog("%#", currentCategory);
}
return self;
}
ok, first thing is ;
[self setCategories:[[NSArray alloc] initWithContentsOfFile:path]];
you have a leak here. just use
categories = [[NSArray alloc] initWithContentsOfFile:path];
Crash occurs in here;
currentCategory = cat;
you have to retain, use;
currentCategory = [cat retain];
These are the problems I see in posted code, if you have not any mistake in the rest of the program, it should be fine with these fixes.
If you are creating your array something like this:
NSArray *tmpArray = [[NSArray alloc] initWithBlah ...];
Make sure that you assign it using the synthesized getter by using this code:
self.categories = tmpArray;
[tmpArray release];
If you do:
categories = tmpArray;
[tmpArray release];
the instance variable will not be retained at all.