Trouble Adding Object To NSMutableArray in For Loop - objective-c

I'm having some trouble adding an object to an NSMutableArray in a for loop. When I try it, I get an error:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
Blockquote
I've looked at other questions very similar to this and they are solved because the array was immutable or they were reading off NSUserDefaults, however I am doing neither of those things.
I download some JSON and parse it using NSJSONSelialization, and the idea is that I have an almost twitter-like stream of activity coming down from the top of the UITableView. Here's my code:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
if (isDownloadingFirstTime){
isDownloadingFirstTime = NO;
dataArray = [Methods parseJSONDataWithData:data];
[mainTableView reloadData];
} else{
[refreshControl endRefreshing];
NSArray *tempArray = [Methods parseJSONDataWithData:data];
for (int num = 0; num < tempArray.count; num++){
[dataArray addObject:[tempArray objectAtIndex:num]];
NSArray *indexPathArray = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:0]];
[mainTableView beginUpdates];
[mainTableView insertRowsAtIndexPaths:indexPathArray withRowAnimation:UITableViewRowAnimationTop];
[mainTableView endUpdates];
}
}
data = [NSMutableData data];
}
I get the exception on the first line of the for loop, when I try to addObject:
I in no way am using NSUserDefaults in this class and I'm 100% sure that dataArray is an NSMutableArray.
Any help or advice is appreciated!

Your dataArray isn't a mutable array. You might did type the pointer as one, but that doesn't make the object itself mutable. The easiest would by to wit a mutable copy tho dataArray.
dataArray = [[Methods parseJSONDataWithData:data] mutableCopy];

Related

"unrecognized selector sent to instance" for NSArrayI

I have the following code which gives the "unrecognized selector sent to instance" error for the NSArray. I've not been able to figure this out and feel its something simple I'm missing here. I can post more if needed.
Quote *myQuote;
NSArray *myQuotes = theSubject.quotes;
//START LOOP HERE
for (myQuote in myQuotes){
NSLog(#" excerpt = %#", myQuote.excerpt);
NSLog(#" desc2 = %#", myQuote.desc2);
NSLog(#" quote_date = %#", myQuote.quote_date);
NSLog(#" myQuote = %#", myQuote);
I believe the problem is in this function which returns an array of Quotes:
- (NSArray *) getQuotesFromSubId:(NSInteger )subId {
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
self.quoteMaps = [appDelegate quoteMaps];
self.quotes = [appDelegate quotes];
//get the quote_ids from quote_map for this subject_id
NSString *stringOfSubjectId = [NSString stringWithFormat:#"%ld", (long)subId];
NSPredicate *filterSubjectId = [NSPredicate predicateWithFormat:#"subject_id == %#", stringOfSubjectId];
NSArray *quoteMapSection = [self.quoteMaps filteredArrayUsingPredicate:filterSubjectId];
NSMutableArray *quoteSection = [[NSMutableArray alloc] init];
NSArray *quoteToAdd = [[NSArray alloc] init];
for (QuoteMap *qm in quoteMapSection){
//get the quote_ids from quote_map for this subject_id
NSPredicate *filter = [NSPredicate predicateWithFormat:#"quote_id == %#", qm.quote_id];
quoteToAdd = [self.quotes filteredArrayUsingPredicate:filter];
[quoteSection addObject:quoteToAdd];
}
return quoteSection;
}
This is where I call it:
QuotesAppDelegate *appDelegate = (QuotesAppDelegate *)[[UIApplication sharedApplication] delegate];
NSArray *myQuotes = [appDelegate getQuotesFromSubId:selectedSubject.subject_id];
NSMutableArray *mArray = [appDelegate createMutableArray:myQuotes];
selectedSubject.quotes = mArray;
NSMutableArray *mutableArray = [appDelegate createMutableArray:myQuotes];
selectedSubject.quotes = mutableArray;
I got the following error
2016-02-23 00:24:20.383 Quotes[10631:3698114] -[__NSArrayI excerpt]: unrecognized selector sent to instance 0x15ebbeff0
2016-02-23 00:24:29.164 Quotes[10631:3698114] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI excerpt]: unrecognized selector sent to instance 0x15ebbeff0'
*** First throw call stack:
(0x182b55900 0x1821c3f80 0x182b5c61c 0x182b595b8 0x182a5d68c 0x100078b2c 0x1000642d0 0x187cb17f4 0x187cb1f8c 0x187b9fc90 0x187ba2e88 0x187977284 0x187883394 0x187882e90 0x187882d18 0x185259c00 0x10011dbb0 0x100123658 0x182b0cbb0 0x182b0aa18 0x182a39680 0x183f48088 0x1878b0d90 0x100040398 0x1825da8b8)
libc++abi.dylib: terminating with uncaught exception of type NSException
You are sending -excerpt to the members (myQuote) of myQuotes. The runtime says that NSArray (NSArrayI is an internal subclass) instances cannot understand -excerpt.
So the type of the member is NSArray. We cannot know, why you have instances of NSArray in the array MyQuotes, because we do not see that code. Likely that happened when you tried to add new quotes to the quotes property and incidentally added the whole array instead of its members.
To your edit:
This is wrong:
NSArray *quoteToAdd = [[NSArray alloc] init]; // This is an array. It identifier should be quote*s*ToAdd
// BTW: This above code is meaningless, because you do not need to create an array instance. Simply omit "[[NSArray alloc] init]". But this is not your problem.
for (QuoteMap *qm in quoteMapSection){
…
quoteToAdd = [self.quotes filteredArrayUsingPredicate:filter]; // filtered array returns an *array*
[quoteSection addObject:quoteToAdd]; // You add the *array* instead of the member of the array.
}
What you get back is an array. Then you add the array itself (not its members) to the existing array. As result you get an array that contains an array.
Simply change …
[quoteSection addObject:quoteToAdd];
… to:
[quoteSection addObjectsFromArray:quoteToAdd];
(And change the reference name to a plural form for better readability.)

NSMutable array error & showAnnotations

I have the following situation. I developed an app that frequently retrieves values for POI's that I want to show as annotations on a map. For this I wrote the method below, called after when a new set of POIs was retrieved:
-(void)showAnnotation{
[self removeAllPinsButUserLocation];
annotationArray = [[NSMutableArray alloc] init];
for (Poi *poi in [parser.pois allValues]) {
myAnnotation *arr = [[myAnnotation alloc] init];
arr.title = [NSString stringWithFormat:#"%# (%#)",poi.description, sensor.name];
arr.subtitle = [NSString stringWithFormat:#"%#, %# %#",poi.street, poi.postalcode, poi.city];
arr.coordinate = CLLocationCoordinate2DMake(poi.lattitude, poi.longitude);
[annotationArray addObject:arr];
arr = nil;
}
[self.mapView addAnnotations:annotationArray];
[self.mapView showAnnotations:annotationArray animated:NO];
}
Problem is that I get an error (Terminating app due to uncaught exception 'NSGenericException', reason: '* Collection <__NSArrayM: 0x14ebe9b0> was mutated while being enumerated.')
However only if I set animated:NO in the last line, but not when set to animated:YES...
Does anyone have an answer for this?
Thanks for replying!
Eelco

NSUserDefaults and storing values

Hi I am trying to store an array into NSUserDefaults but I am having troubles. The method accepts an NSDictionary which I will store into an array that i will store into NSUSerDefaults. The problem is when I make a mutableCopy it says its a dictionary and not of type NSMutable array? This method is the first time I would be calling NSUserDefaults so I am unsure why the error is happening? Here is the code thanks
+(void) getRecentPhoto:(NSDictionary *)recentPhoto{
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
//stores it as a dictionary? error happens here
NSMutableArray* recentPhotos = [[defaults objectForKey:#"recentPhoto"] mutableCopy];
NSLog(#"%#", [recentPhotos class]);
if(!recentPhotos) recentPhotos = [NSMutableArray array];
BOOL copy = NO;
//these will crash the program
NSLog(#"%#", [[recentPhotos objectAtIndex:0] objectForKey:#"id"]);
NSLog(#"%#", [recentPhoto objectForKey:#"id"]);
//this checks if it has been stored before by using an id key
for(int i =0; i < [recentPhotos count]; i++){
if ([[[recentPhotos objectAtIndex:i] objectForKey:#"id"] isEqualToString:[recentPhoto objectForKey:#"id"]] ) {
copy = YES;
}
}
if(copy ==NO)
[recentPhotos addObject:recentPhoto];
[defaults setObject:recentPhoto forKey:#"recentPhoto"];
[defaults synchronize];
}
This is the error
NSInvalidArgumentException', reason: '-[__NSCFDictionary objectAtIndex:]: unrecognized selector
I believe the problem is, in the end of this method, you try to store recentPhoto, which is a dictionary, into user default instead of recentPhotos, the mutable array you want to store.
Actually, I think it will not crash at the first time this method is called since recentPhoto has not been stored in user default. But after that, it will.

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: NSArrayM objectForKey

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayM objectForKey:]: unrecognized selector sent to instance 0x12e654c0.
I am loading data on tableview from Array that has two dictionary objects. Dictionary object contains 2 nsstring object when ViewDidLoad called the code is below
while (sqlite3_step(statement) == SQLITE_ROW) {
NSString* delID = [NSString stringWithUTF8String:(const char *) sqlite3_column_text(statement, 0)];
NSString* name =[NSString stringWithUTF8String:(const char *) sqlite3_column_text(statement, 1)];
NSMutableDictionary *dict=[NSMutableDictionary dictionary];
[dict setObject:delID forKey:#"delID"];
[dict setObject:name forKey:#"name"];
[self.arr addObject:dict];
It never crash for first row but on second row indexPath.row==1 it always crash please see the screen shot below. Thanks for help
Here is I am filling self.arr again
-(void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
{
if ([datePicker1.CurrentDate length]) {
UIButton *btn=(UIButton*)[self.view viewWithTag:btnTag];
[btn setTitle:datePicker1.CurrentDate forState:UIControlStateNormal];
}
if ([self.searchDel.arrSelectDelAdd count ])
{
[self.arr addObject:self.searchDel.arrSelectDelAdd];
[self.tblDelivery reloadData];
}
}
Remove the if condition from your code. As it is just adding first dictionary into the array not the second one.
//if (i==0){
[self.arr addObject:dict];
//}
Try this :)
Sorry to bother all of you Actually the problem is that I am adding second or another object like
[self.arr addObject:self.searchDel.arrSelectDelAdd]; //Incorrect, that is adding NSArray type not dictionary
then getting values in cellForRowAtIndexPath
NSDictionary *dict=[arr objectAtIndex:indexPath.row];//here dict will contain NSSArray type not dict type object its causing to crash app.
cell.lblID.text=(NSString*)[dict objectForKey:#"delID"];// at this line.
cell.lblName.text=(NSString*)[dict objectForKey:#"name"];
[self.arr addObjectsFromArray:self.searchDel.arrSelectDelAdd]; // correct, thats the solution giving contents of NSSArray and now working my code

Mutated array while being enumerated

The error I am getting is
Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <CALayerArray: 0x7c85080> was mutated while being enumerated.'
What I understand from NSGenericException is that I am removing something from my array while it is enumerating. Still knowing that and looking around I can not seem to resolve my issue. Here is the code below.
-(void)tableView(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
//Checks if message has been read. If message has not it updates the unreadMessages array
if ([unreadMessage containsObject:[NSString stringWithFormat:#"m%#",[[storage_array objectAtIndex:indexPath.row] objectForKey:#"id"]]] == TRUE && [[[storage_array objectAtIndex:indexPath.row] objectForKey:#"itemtype"] isEqualToString:#"message"] == TRUE){
//Unread
[unreadMessage removeObject:[NSString stringWithFormat:#"m%#",[[storage_array objectAtIndex:indexPath.row] objectForKey:#"id"]]];
[unreadMessage writeToFile:[self saveUnreadMessages] atomically:YES];
//Read
[readMessage addObject:[NSString stringWithFormat:#"m%#",[[storage_array objectAtIndex:indexPath.row] objectForKey:#"id"]]];
[readMessage writeToFile:[self saveReadMessages] atomically:YES];
[tableView reloadData];
}
else if ([unreadNewsletter containsObject:[NSString stringWithFormat:#"n%#",[[storage_array objectAtIndex:indexPath.row] objectForKey:#"id"]]] == TRUE && [[[storage_array objectAtIndex:indexPath.row] objectForKey:#"itemtype"] isEqualToString:#"newsletter"] == TRUE){
//Unread
[unreadNewsletter removeObject:[NSString stringWithFormat:#"n%#",[[storage_array objectAtIndex:indexPath.row] objectForKey:#"id"]]];
[unreadNewsletter writeToFile:[self saveUnreadNewsletters] atomically:YES];
//Read
[readNewsletter addObject:[NSString stringWithFormat:#"n%#",[[storage_array objectAtIndex:indexPath.row] objectForKey:#"id"]]];
[readNewsletter writeToFile:[self saveReadNewsletters] atomically:YES];
[tableView reloadData];
}
}
You shouldn't modify the array you are iterating over. If you intent to do so, you should iterate over a copy of it:
for (id item in [array copy])
{
…
}
(with ARC. [[array copy] autorelease] without ARC.)
If needed, you can check if the item is still in the mutable array before doing anything with it.
Alternatively, you could build up a new NSMutableArray of the objects needing to be removed while iterating through your original mutable array and then, after the loop completes, call
[originalMutableArray removeObjects:newArrayContainingObjectsNeedingToBeRemoved];