table view controller cs193p iTunesU 9 lesson - ios

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.

Related

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

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

NSUserDefaults UITableView

I am a novice programmer. Because i'm a newby at Stackoverflow I cannot comment someones anwser yet. I got a question about this post : How to use NSUserDefaults to save data from ViewController and retrieve it to TableViewController when app reruns
I try to solve the problem and I read many topics on Stackoverflow, but I couldn't find the solution. I have the same code as Rhenzzz and I implemented the solution explained by Jay.
I have an error in cellForRowAtIndexPath method at the line NSString *itemWish = itemDictionary[#"itemWish"];.
Error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[WishListItem objectForKeyedSubscript:]: unrecognized selector sent to instance 0x9dc4d80'
My full method is :
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"wishlistCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *itemDictionary = self.wishlistItem[indexPath.row];
NSString *itemWish = itemDictionary[#"itemWish"];
cell.textLabel.text = itemWish;
return cell;
}
EDIT :
I forget to precise that my error occurs when I add a new item. The tableview displays datas when I launch the App. But when I add data on my ViewController and then save & go back to the Tableview by clicking on my Done button, I have the error describe above.
So I put some NSLog in cellForRowAtIndexPath : NSLog(#"Display the dictionary:%#",itemDictionary ); and NSLog(#"Display the item:%#",itemWish );. Everything is right.
So my problem comes certainly from the IBAction :
- (IBAction)unwindToList:(UIStoryboardSegue *)unwindSegue {
AddItemViewController *source = [unwindSegue sourceViewController];
WishlistItem *item = source.wishItem;
if (item != nil) {
[self.wishlistItem addObject:item];
[self.tableView reloadData];
}
}
If I delete the [self.tableView reloadData]; the error disappears but obviously my Tableview is not auto-updated.
Thanks for your help !
Given the error, it is clear that self.wishlistItem is an array of WishListItem objects and not an array of dictionaries. You should change these two lines:
NSDictionary *itemDictionary = self.wishlistItem[indexPath.row];
NSString *itemWish = itemDictionary[#"itemWish"];
to:
WishListItem *wishListItem = self.wishlistItem[indexPath.row];
NSString *itemWish = wishListItem.somePropertyGivingTheDesiredValue;
where obviously you need to use the appropriate property on wishListItem to get the value you want from it.

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.

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.

Why can't I use the initialized array in tabbed application?

In a tabbed application developed in Xcode 4.2, I found a confusing problem: In one of tabs, there is a tableview to show something like index. So I initialized an array in viewDidLoad() method. for example:
- (void)viewDidLoad
{
NSArray *array = [NSArray arrayWithObjects:#"abc", #"def", #"ghi", #"jkl", #"mno", nil];
self.arrayList = array;
[array release];
[super viewDidLoad];
}
Then I use this arrayList in other methods:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayList 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];
}
cell.textLabel.text = [arrayList objectAtIndex:indexPath.row];
return cell;
}
But Xcode gives me the "EXC_BAD_ACCESS" signal every time I run it. I put some breakpoints, and found that the array was created successfully in viewDidLoad(), but before running the method cellForRowAtIndexPath:(NSIndexPath *)indexPath, it became a freed object. That's the reason why I got that signal and the application crashed. So, how to solve this problem?
Btw, the view controller where the problem occurs is created from UIViewController, not UITableViewController. But I put a table view and linked its datasource and delegate to File's Owner. Does that matter?
You must not release the array. The +arrayWithObjects: convenience method returns an unowned array. You never took ownership of the array, therefore you must not relinquish ownership. Remove the the line [array release] and you will not see this error anymore (at least not for that reason).

Resources