Deleting multiple selections from UITableView throws exception after scrolling (reuse issue?) - ios

I'm trying to delete multiple selections from a table view. Everything works fine until I scroll up or down, then it crashes and throws an exception.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
Object can not be nil
This is how I am deleting the objects :
- (IBAction)deleteObjects:(id)sender {
NSArray *selectedRows = [self.tableView indexPathsForSelectedRows];
BOOL deleteSpecificRows = selectedRows.count > 0;
if (deleteSpecificRows)
{
NSMutableArray *stringsOfObjects = [NSMutableArray new];
for (NSIndexPath *selectionIndex in selectedRows) {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:selectionIndex];
[stringsOfObjects addObject:cell.textLabel.text];
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths objectAtIndex:0];
NSString *plistPath = [path stringByAppendingPathComponent:#"AlertSubscriptions.plist"];
NSMutableArray *array = [[[NSMutableArray alloc] initWithContentsOfFile:plistPath] mutableCopy];
[array removeObjectsInArray:stringsOfObjects];
[self.alertSubArray removeObjectsInArray:stringsOfObjects];
[array writeToFile:plistPath atomically:YES];
[self.tableView deleteRowsAtIndexPaths:selectedRows withRowAnimation:UITableViewRowAnimationAutomatic];
}
Again this all works fine, unless I scroll up/down to select/deselect more cells so I subclassed my cells because I read that won't reuse cells on SO.
For reference:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SubscriptionsTableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:nil];
if (cell == nil) {
cell = [[SubscriptionsTableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:nil];
}
cell.textLabel.text = [self.alertSubArray objectAtIndex:indexPath.row];
cell.textLabel.textAlignment = NSTextAlignmentLeft;
return cell;
}
Ive tried it with a static cell and without. I've tried setting dequeueReusableCellWithIdentifier to a static cell and without. Neither work when I scroll
static NSString *CellIdentifier = #"Cell";
Error log:
2015-06-28 15:46:19.379 *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
*** First throw call stack:
(0x186d3c2d8 0x1985680e4 0x186c234c0 0x10017e7d8 0x18b7b1404 0x18b79a4e0 0x18b7b0da0 0x18b7b0a2c 0x18b7a9f68 0x18b77d18c 0x18ba1e324 0x18b77b6a0 0x186cf4240 0x186cf34e4 0x186cf1594 0x186c1d2d4 0x1904336fc 0x18b7e2fac 0x1001646d4 0x198be6a08)
libc++abi.dylib: terminating with uncaught exception of type NSException
EDIT
So after trying what the others have told me to do I have done the following:
Set an exception breakpoint. The line that populates after crash is the [stringsOfObjects addObject:cell.textLabel.text]; line.
i have made sure my cellForRowAtIndexPath method was set up properly now:
}
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
cell.backgroundColor = [UIColor whiteColor];
[self.tableView setSeparatorStyle:UITableViewCellSeparatorStyleSingleLine];
[self.tableView setRowHeight:45];
cell.textLabel.text = [self.alertSubArray objectAtIndex:indexPath.row];
cell.textLabel.textAlignment = NSTextAlignmentLeft;
return cell;
}
After that I still get a crash at adding the objects to the NSMutableArray so I looked in the debugger and made sure my UITableViewCell isn't nil like Robot mentioned to me, and it looks like it is but I don't know where to go from here: because in my mind it is not nil
As you can see, I have selected 6 rows but it only added 2 objects. I don't know why this is so difficult, why is it nil when some aren't? And why can I delete them perfectly fine without scrolling to select more?

So, after an extensive discussion in the comments the problem seems to be the following:
The logic of [stringsOfObjects addObject:cell.textLabel.text]; in the deleteObjects: method is wrong. This is because it is taking the text direct from the cells rather than the array backing store that populates the cells.
Cells can be scrolled offscreen and re-used so the text in them is no longer correct and, in fact, the cell no longer "exists" as it has been reused. If the cell doesn't "exist" an empty cell will be created where the text field might be nil. Note that cell re-use is a good thing; don't create cells and never re-use them or you will run out of memory fast.
Instead, take the text from your backing store that populates the cells themselves rather than from the cell directly. I would expect code something like:
[stringsOfObjects addObject:[self.alertSubArray objectAtIndex:selectionIndex.row]];

Related

NSInvalidArgumentException: "Unrecognized selector sent to instance"

I'm facing a problem that I not truly understand the reason. The exception does not give me a clue to understand the problem.
I want to modify content of UILabel at my interface according to the data given in myArray. However, as the line I specified at function "cellForRowAtIndexPath" the program fires an exception.
What is the reason for this problem?
#property (strong, nonatomic) NSMutableArray *myArray; //
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[myArray addObject:#{#"field1": #"myfield1"}]
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger) section {
return self.myArray.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"CellIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
UILabel *myLabel = (UILabel *)[cell viewWithTag:100]; // myLabel is successfully created with the given viewWithTag
NSLog(#"Object at indexpath.row: %ld", (long)indexPath.row); // Object at indexpath.row: 0
NSLog(#"The obj of the array = %#",[self.myArray objectAtIndex:indexPath.row] ); // The obj of the array = {field1: #"myfield1"}
myLabel.text = [[self.myArray objectAtIndex:indexPath.row] objectForKey:#"field1"]; // this part fires the exception given below.
return cell;
}
//getter for myArray
-(NSMutableArray *)myArray{
if(! _myArray){
_myArray = [[NSMutableArray alloc] init];
}
return _myArray;
}
The error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFArray objectForKey:]: unrecognized selector sent to instance 0x1459d1f0'
Rather than tell you what to change your code, I'll give you some pointers so you will hopefully be able to resolve your problems in the future a bit easier.
First, the error message you have is this
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFArray objectForKey:]: unrecognized selector sent to instance 0x1459d1f0'
The exception message is the key point here
unrecognized selector sent to instance
If you search for this message using the search engine of your choice, you'll see that this means you are calling a method on an object that doesn't respond to that method. The error message also tells you the method you are trying to call, and on which type of object you are calling it.
[__NSCFArray objectForKey:]
If you look at the documentation for the NSArray object, you'll see that there is no objectForKey method available for that.
What you should now do is set a breakpoint in your code (if you don't know about breakpoints, go off and read about them - they are IMPORTANT for debugging) and step through until you hit the line that is throwing the exception. At this point, you can inspect the objects you have and see what the types are. You should then be able to work out what you should do with the coding.
[__NSCFArray objectForKey:]: unrecognized selector sent to instance 0x1459d1f0'
You can't call objectForKey for NSMutableArray
Maybe you should use NSDictionary if you need to use objectForKey:
or you can use array of arrays
ex:
NSArray *array = [[NSArray alloc] initWithObjects:#"field1Value",#"field2Value",#"field3Value",nil];
[self.myArray addObject:array];
Now , when you need to retrieve some fields value, then just call the index in the array
NSArray *array = [self.myArray objectAtIndex:[indexPath row]];
NSString* valueField1 =[array objectAtIndex:0];
Hope this will help:)
Edit:
If you need to use NSDictionary
self.myArray=[[NSMutableArray alloc]init];
dict=[[NSDictionary alloc] initWithObjectsAndKeys:#"value",#"KeyName",nil];
[self.myArray addObject:dict];
myLabel.text = [[self.myArray objectAtIndex:indexPath.row] objectForKey:#"KeyName"];
The exception does not give me a clue to understand the problem.
I disagree, the exception tells you exactly what you did wrong. It tells you that you sent objectForKey: to an array instead of to a dictionary. The only line where I see you using objectForKey: is this one.
myLabel.text = [[self.myArray objectAtIndex:indexPath.row] objectForKey:#"field1"];
which means that [self.myArray objectAtIndex:indexPath.row] is an array, not a dictionary. I don't know how that happened because nowhere in your code that you show us is there anything that adds an object to self.myArray. In particular, this doesn't:
[myArray addObject:#{#"field1": #"myfield1"}]
It should say
[self.myArray addObject:#{#"field1": #"myfield1"}];
I suspect that was just a copy error though because you also forgot the semicolon. Note that just putting an underscore on the front of myArrayisn't any good because you rely on the accessor to initialise the array.
If you store NSDictionary or NSMutableDictionary to array then then the code will work for you. If you want to add NSDictionary to array you should use: [[NSDictionary alloc] initWithObjectsAndKeys:#"value1", #"key1", #"value2", #"key2", nil]
To anyone facing a similar exception, check all your IBActions and IBOutlets!
I deleted a function but forgot to right click on the button on the Main.story board to delete the outlet/action there.
example below:
Here we have 2 outlets, called "test" and "test2" respectfully, for the button in the upper left corner
Here I deleted the code for "test2" outlet, but when I rightclick in the button the reference is still there.

Error with detailTextLabel from NSArray in UITableView

I'm creating a Table View with the Detail Right style of cells (prototype cells). In implementing the self.detailLabelText.text on an array I have I'm receiving an error message when I attempt to load the table. Below is the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:simpleTableIdentifier];
}
switch (pickerSelection)
{
case 0:
cell.textLabel.text = [visitList1 objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [windows1 objectAtIndex:indexPath.row];
break;
case 1:
cell.textLabel.text = [visitList2 objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [windows2 objectAtIndex:indexPath.row];
break;
}
return cell;
}
I'm receiving the error message:
Terminating app due to uncaught exception 'NSRangeException', reason: '* -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
The table loads correctly with the main textLabel, and I can load a string after cell.detailTextLabel = so I know that my setup is correct. I also can NSLog both of the arrays I'm trying to load, just not in the detailLabelText.
I know the error message has to due with the expected length of an array, I just can't figure out what or where. Sorry for an elementary question. I'm a noob.
UPDATE: Here is my numberOfRowsInSelection method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
switch (pickerSelection)
{
case 0:
return [visitList1 count];
return [windows1 count];
break;
case 1:
return [visitList2 count];
return [windows2 count];
break;
}
return 10;
}
Each of the arrays have over 3 items. i threw that last "return 10;" in there just in case something wasn't working right with the switch statement, but it doesn't seem to matter.
Update 2: When I replace my windows1 array with static strings they populate in the detail area of the table. That's great (although I now seem to have some sort of problem with my original window arrays).
Either the windows1 array or windows2 array only has 2 items in it, and your table datasource is telling it there are 3 rows to display. As Kevin asked, edit your question with the -tableView:numberOfRowsInSection: method you implemented, as the problem is almost certainly in there.

App crashing when it executes the table delegate method

When I run the search function in aepub reader my app crashes. It enters the cellfor row at index method and when it executes NSLOg(#"%#",hit.neighbourText) it shows exception.
(UITableViewCell *)tableView:(UITableView *)tableView1 cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView1 dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.textLabel.adjustsFontSizeToFitWidth = YES;
NSLog(#"indexpath%d",indexPath.row);
NSLog(#"%#",[results objectAtIndex:[indexPath row]]);
hit = (SearchResult*)[results objectAtIndex:[indexPath row]];
if([results count]>0) {
NSLog(#"%#",hit.neighboringText);
cell.textLabel.text = [NSString stringWithFormat:#"...%#...", hit.neighboringText];
cell.detailTextLabel.text = [NSString stringWithFormat:#"Chapter %d - page %d", hit.chapterIndex, hit.pageIndex+1];
return cell;
}
}
I am getting some value for hit.neighboringText but after that, I reload my tableview then the following exception will be raised, why?
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFConstantString
neighboringText]: unrecognized selector sent to instance 0x1481c4'
*** First throw call stack:
It's because hit is actually an NSString object and not a SearchResult object as you expect:
hit = (SearchResult*)[results objectAtIndex:[indexPath row]];
The clue is in the exception text:
-[__NSCFConstantString neighboringText]: unrecognized selector sent to instance ...
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
No amount of casting to SearchResult is going to change that.
EDIT: Actually anywhere you see a cast you should be suspicious of the actual object you are dealing with. If you aren't sure then check it with isKindOfClass:.
it means that hit = (SearchResult*)[results objectAtIndex:[indexPath row]]; returns a ConstantString and not SearchResult object
Best would be checking if hit is the same class type as SearchResult before getting the value from neighboringText
you can try like something like this:
if([hit isKindOfClass:[SearchResult Class]]){
// do something with hit
}
else{
// different class
}
The answer to your problem lies in the error message:
unrecognised selector sent to instance 0x1481c4.
What you need to do next is print the value of that address via po 0x1481c4. It would appear that it is not actually a string, but you are not showing that code.
I guess there are two possibilities:
hit is not a SearchResult object but a String object
hit or the results array is not owned anymore / released but not set to nil and point garbage, which I believe is the case because I have experienced it before
I think you need to make sure that the array is not autoreleased/released at that point (for example if you are creating it with [NSArray arrayWith...] it is autoreleased, you might not own it inside cellForRowAtIndexPath) and the hit object is properly initialized before giving it to the results array.

table view controller cs193p iTunesU 9 lesson

Hi I'm studying a 9TH lesson of iTunesU CS193P about table view and compiler report me this error
NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'
my simulator is iPad 6.1
so I have
a class called GraphViewController.m
#define FAVORITE_KEY #"GraphViewController.Favorite"
- (IBAction)addFavorite:(id)sender
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray *favorites = [[defaults objectForKey:FAVORITE_KEY] mutableCopy];
if (!favorites) favorites = [NSMutableArray array];
[favorites addObject:self.program];
// NSLog(#"contenuto favorites %#",favorites);
[defaults setObject:favorites forKey:FAVORITE_KEY];
[defaults synchronize];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"GraphTableView"]) {
NSArray *program = [[NSUserDefaults standardUserDefaults]objectForKey:FAVORITE_KEY];
[segue.destinationViewController setPrograms:program];
}
}
(setPrograms is the setter where i have the data to send at my tableviewcontroller called CalculatorTVC.m)
a class called CalculatorTVC.m
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.programs count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cellTable";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
id program = [self.programs objectAtIndex:indexPath.row];
cell.textLabel.text = [#"y = " stringByAppendingString:[CalculatorBrain descriptionProgram:program]];
NSLog(#"contenuto di program %#",program);
return cell;
}
(programs is a public property where I put the data from GraphViewController.m)
In my storyboard I have a split view ...in MasterViewController i have toolbar with bar button item wired with table view controller (CalculatorTVC.m) in popover style identifier is GraphTableView and i have a round rect button that is addFavorite describe here up
the error is
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'
* First throw call stack:
(0x1ca1012 0x10dee7e 0x1ca0deb 0x1d21c4f 0x1d21911 0x4a5b 0x8f65 0xdd8fb 0xdd9cf 0xc61bb 0xd6b4b 0x732dd 0x10f26b0 0x229dfc0 0x229233c 0x22a0238 0x6b6e3 0x4f1476 0x870989a 0x4f2555 0x489ef9 0x46ab99 0x46ac14 0x10f2705 0x262c0 0x262a64 0x10f2705 0x262c0 0x26258 0xe7021 0xe757f 0xe66e8 0x55cef 0x55f02 0x33d4a 0x25698 0x1bfcdf9 0x1bfcad0 0x1c16bf5 0x1c16962 0x1c47bb6 0x1c46f44 0x1c46e1b 0x1bfb7e3 0x1bfb668 0x22ffc 0x24bd 0x23e5)
libc++abi.dylib: terminate called throwing an exception
(lldb)
please help me
thanks for your patience
regards
ok I find where it crashes....
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"cellTable";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
// Configure the cell...
id program = [self.programs objectAtIndex:indexPath.row];
cell.textLabel.text = [#"y = " stringByAppendingString:[CalculatorBrain descriptionProgram:program]];
NSLog(#"contenuto di program %#",program);
return cell;
}
the crash in the line
cell.textLabel.text = [#"y = " stringByAppendingString:[CalculatorBrain descriptionProgram:program]];
if I put NSLOG before this line i see in the output NSLOG result...but if I put NSLOG after this I don't see anything in the output
NSArray *program = [[NSUserDefaults standardUserDefaults]objectForKey:FAVORITE_KEY];
[segue.destinationViewController setPrograms:program];
Here you're passing an immutable array to the destination view controller. If it is expecting a mutable array and tries to modify it, you'll get that crash. You need mutableCopy here as well.
If the property should be a mutable array, you should get compiler warnings. Don't ignore these!
You're crashing on remove, not add, by the way, so you haven't included the right code in your question. Enable exception breakpoints to find the line you're crashing on.

iOS - AQGridView crashes because reuseIdentifier is nil (?)

I have a AQGridView , The "initialization" of the grid works great,
but when i do [gridView reloadData], it will crash with the error
2012-01-03 21:27:40.338 XXX[8454:707] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFDictionary setObject:forKey:]: attempt to insert nil key'
After a couple of tries i've tracked the error coming from AQGridView's enqueueReusableCells. Its trying to get cell.reuseIdentifier and its value is nil for some weird reason.
Here's the code for gridView:cellForItemAtindex:
- (AQGridViewCell *)gridView:(AQGridView *)aGridView cellForItemAtIndex:(NSUInteger)index{
static NSString *CellIdentifier = #"SocialCell";
SocialCell *cell = (SocialCell *) [gridView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil){
cell = [[SocialCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier];
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"SocialCell" owner:nil options:nil];
for(id currentObject in topLevelObjects){
if([currentObject isKindOfClass:[SocialCell class]]){
cell = (SocialCell *)currentObject;
break;
}
}
}
As I said, first run works great, but the second run (reloading the data) , doesnt work as expected. This view includes 7 cells total so it doesn't even actually get dequeued, so i'm not sure why the value would be nil and crash.
Would appreciate any info on this :)
Thanks!

Resources