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];
Related
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];
I am parsing data from server and display this data in my app. This data is a JSON data and it looks like this:
{"getMessages":[{"msgid":"1","message":"Hello.","dateposted":"2012-08-28"}]}
That's when a message is available to be sent, however, if no messages were available, JSON will look like this:
{"status":"No messages available"}
In my app, I use NSJSONSerialization to parse the JSON. Here is how I do it:
if ([data length] > 0)
{
NSDictionary *parsedData = [NSJSONSerialization JSONObjectWithData:data options
NSJSONReadingMutableContainers error:nil];
if (![parsedData objectForKey:#"getMessages"])
{
[self.messageArray addObject:#"No Messages"];
}
else
{
self.messageArray = (NSMutableArray *)[parsedData objectForKey:#"getMessages"];
}
}
As you can see, when the parsedData has no getMessages key, it will add the No Messages in
self.messageArray, but if it has the key, it will add the values related to it.
self.messageArray was the array I used to populate the messageTable. At the end of the download, I put the code [messageTable reloadData].
The problem is this: Reloading the table works if the parsedData contains the key getMessages. However, if the key was not found, reloading the table crashes.
This is my tableView:cellForRowAtIndexPath method:
NSString *tableIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:tableIdentifier] autorelease];
if (tableView == messageTable) //I do this since there is another table I am using
{
NSString *string = [NSString stringWithFormat:#"%#", [self.messageArray objectAtIndex:0]];
if ([string isEqualToString:#"No Messages"])
{
cell.textLabel.text = string;
}
else
{
NSDictionary *dict = [self.messageArray objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%#", [dict objectForKey:#"message"]];
}
}
In messageTable, I check first if the first index of self.messageArray is equal to the string "No Messages", this is to let the user know that no messages can be retrieved. If the
string is not equal, it will then assume that the data inside the array is a dictionary and therefore, it will be parsed to display the message.
After making use of breakpoints and logs, I realized that the crash happens while reloading the table. I inserted a breakpoint and a log at the start of the method tableView:cellForRowAtIndexPath but it never even got there. I tried checking the content of the self.messageArray and it does contain "No Messages".
The crash tells me this error: [__NSCFConstantString objectForKey:]: unrecognized selector sent to instance 0x10ed84
I know that this error is telling me that I am calling the method objectForKey in a NSString, but I really don't know why. Can anyone help me here?
try this
[self.messageArray removeAllObjects];
[self.messageArray addObject:[parsedData setObject:#"No Messages" forKey:#"getMessages"]];
mostly the dictionary is not setting for the key.. just check..
Also,
NSDictionary *diction = [self.messageArray objectAtIndex:0];
NSString *string = [NSString stringWithFormat:#"%#", [diction objectForKey:#"getMessages"]];
if ([string isEqualToString:#"No Messages"])
{
cell.textLabel.text = string;
}
else
{
NSDictionary *dict = [self.messageArray objectAtIndex:indexPath.row];
cell.textLabel.text = [NSString stringWithFormat:#"%#", [dict objectForKey:#"message"]];
}
First make sure that messageArray is defined as NSMutableArray... and if you are reloading your parsedData from time to time then try this
if (![parsedData objectForKey:#"getMessages"])
{
[self.messageArray removeAllObjects];
[self.messageArray addObject:#"No Messages"];
}
else
{
[self.messageArray removeAllObjects];
self.messageArray = [[parsedData objectForKey:#"getMessages"] mutableCopy];
}
because i'm a newby at Stackoverflow i cannot comment someones anwser yet. (my reputation is 16..). I got a question about this anwser: How do I put this JSON data into my table view? Please help me, I'm living in a nightmare :)
Fulvio sais you have to use [eventNameList addObject:event]; and [eventNameList objectAtIndex:indexPath.row]; to store and get the event data but. addObject is an NSMutableSet method and objectAtIndex:indexPath.row is not. So i cannot use this method to get the data from the NSMutableSet.
Besides that, i can use the count methods neither.
Any Idea's ?
Assuming you have an NSDictionary, you could use the [dictionary allKeys] method to retrieve an array with all keys (lets call it keyArray for now). For the rowCount you could return the count of objects in this keyArray. To get the item that needs to be displayed in the cell you could use [dictionary objectForKey:[keyArray objectAtIndex:indexPath.row]]] to get the appropriate dictionary for the displayed cell.
In code:
// use the keyArray as a datasource ...
NSArray *keyArray = [jsonDictionary allKeys];
// ------------------------- //
// somewhere else in your code ...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [keyArray count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
// set some cell defaults here (mainly design) ...
}
NSString *key = [keyArray objectAtIndex:indexPath.row];
NSDictionary *dictionary = [jsonDictionary objectForKey:key];
// get values from the dictionary and set the values for the displayed cell ...
return cell;
}
#Tieme: apparantly the URL you use already returns an array, you don't really need to process a dictionary (you could just use the array as the dataSource), check out the following:
SBJSON *json = [[[SBJSON alloc] init] autorelease];
NSURL *url = [NSURL URLWithString:#"http://www.my-bjoeks.nl/competitions/fetchRoutes/25.json"];
NSString *string = [[[NSString alloc] initWithContentsOfURL:url] autorelease];
NSError *jsonError = nil;
id object = [json objectWithString:string error:&jsonError];
if (!jsonError) {
NSLog(#"%#", object);
NSLog(#"%#", [object class]); // seems an array is returned, NOT a dictionary ...
}
// if you need a mutableArray for the tableView, you can convert it.
NSMutableArray *dataArray = [NSMutableArray arrayWithArray:object]
eventNameList should be defined as an NSMutableArray, not an NSMutableSet. NSMutableArray responds to both -addObject (it puts the new object at the end of the array) and -objectAtIndex: and when you think about it, a table view is essentially an ordered list and so is an array whereas a set is not.
LUCKY:)
Assuming that you might be having nsmutablearray of nsdictionary.
In such case you can get data using:
[dictionary objectforkey:#"key"] objectAtIndex:indexpath.row]
I got same warning here “local declaration hides instance variable” warning
but I got more problems...
Here is my code
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.someaddress.php"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
NSLog(#"\n\nCONNECTION: %#", theConnection);
NSData *returnData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:nil];
NSString *listFile = [[NSString alloc] initWithData:returnData encoding:NSASCIIStringEncoding];
NSMutableArray *plist = [[NSMutableArray alloc] init];
plist = [listFile propertyList];
NSLog( #"\n 1111 plist is \n%#", plist );
//I can get a plist format data here,But nothing in 2222
NSLog(#"Now you see me tableView Row Count");
NSLog(#"TOTAL PLIST ROW COUNT IS = %i", [plist count]);
// Return the number of rows in the section.
return [plist count];
}
and I got Warning here"Local declaration of 'plist' hides instance variable"
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"LightCell";
LightCell0 *cell =(LightCell0 *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[LightCell0 alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell…
NSLog(#"Now you see me Load Data %i", indexPath.row);
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
//I try to get list data here But RETURN NULL
NSLog( #"\n 2222 plist is \n %#", plist);
switch (indexPath.row) {
case 0:
if ([plist valueForKey:#"nodeStatus"] == 0){
cell.lightImageView.image = [UIImage imageNamed:#"lightOff.png"];
NSLog(#"value for key Node Status : %#" ,[self.plists Valuefokey:#"nodeStatus"]);
//also return NULL !!
}
else if([self valueForKey:#"nodeStatus"] == 1){
cell.lightImageView.image = [UIImage imageNamed:#"lightOn.png"];
}
break;
case 1:
cell.lightLocation.text =[plist valueForKey:#"nodeName"] ;
if ([plist valueForKey:#"nodeStatus"] == 0){
cell.lightImageView.image = [UIImage imageNamed:#"lightOff.png"];
}
else if([plist valueForKey:#"nodeStatus"] == 1){
cell.lightImageView.image = [UIImage imageNamed:#"lightOn.png"];
};
break;
default:
break;
}
return cell;
}
This is the tow items I create in a plist
{
category = Light;
nodeID = 1;
nodeName = "Living Room";
nodeStatus = 0;
nodeTrigger = 0;
nodeType = "light_sw";
},
{
category = Light;
nodeID = 2;
nodeName = Kitchen;
nodeStatus = 0;
nodeTrigger = 0;
nodeType = "light_sw";
}
So that's my question ,Why can't I pass "plist" from
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
...
}
to
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
...
}
and I use NSMutableArray *plist = [[NSMutableArray alloc] init];
But still appear "Local declaration of 'plist' hides instance variable"
???
hope someone can figure out this problem
Best Regards !
and I got Warning here"Local declaration of 'plist' hides instance variable"
Well, then, you should fix that.
The warning is telling you that you've declared two variables named plist: One local to this instance method, and the other an instance variable. The local variable, having a narrower scope, hides the instance variable, so that when you refer to plist in the method, you are referring to the local variable. This means that you cannot access anything stored in the instance variable by another method, nor store anything in it for another method to retrieve.
The solution is either to kill off or to rename the local variable. If the latter is what you want, use Xcode's “Edit All in Scope” feature.
Also:
NSMutableArray *plist = [[NSMutableArray alloc] init];
plist = [listFile propertyList];
Creating the array on the first of those lines is redundant, because you immediately replace your pointer to that array with the pointer to another array, returned by propertyList. Thus, you never use and you leak the first array. You should at least cut out the creation of the first array, and you should probably cut out the entire first line (thereby cutting out both the first array and the local variable).
Here is the code I fix the warning ,the program can build without any warning
it also can display the result after reading the plist in tableview
1.Load the plist:
- (void)viewDidLoad {
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www. someaddress.php"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
NSData *returnData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:nil];
NSString *listFile = [[NSString alloc] initWithData:returnData encoding:NSASCIIStringEncoding];
plist = [listFile propertyList];
}
2.return the number to rows
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [plist count];
}
3.read the plist data to show result in cells
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"LightCell0";
LightCell0 *cell =(LightCell0 *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[LightCell0 alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell…
[cell setSelectionStyle:UITableViewCellSelectionStyleNone];
int i;
for (i=0; i<[plist count]; i++) {
//Get nodeName
if(indexPath.row == i)
{
cell.lightLocation.text = [[[plist objectAtIndex:i] valueForKey: #"nodeName"]description];
//Get Light Status to show the image
if ([[[plist objectAtIndex:i] valueForKey: #"nodeStatus"] intValue] == 0){
cell.lightImageView.image = [UIImage imageNamed:#"lightOff.png"];
}
else if([[[plist objectAtIndex:i] valueForKey: #"nodeStatus"] intValue] == 1){
cell.lightImageView.image = [UIImage imageNamed:#"lightOn.png"];
cell.lightSwitch.on=YES;
}
}
}
return cell;
}
It can get the right data ,and display the correct result in the tableview cells
BUTTTTTTT
If you scroll up the tableview,it's ok,when you on the top it will scroll down Automatically
When you "scroll down" the tableview,program crash ???
WHY ??? did I write anything wrong ???
plist = [listFile propertyList];=====>self.plist = [listFile propertyList];
THAT IS CORRECT
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//NSLog(#"Array: %#",rows);
return [rows count];// AT THIS LINE
}
Program received signal: “EXC_BAD_ACCESS”
THANKS FOR THE REPLY
Actually I have attached it to the WebPage By NSUrl where I have made a PHP array and I have created a NSLOG where I am getting the Values in the array form but When It exceute the line return [rows count];. It gives error when I am writting statically return 2; then it execute. I am explaining to you what I am doing. I am initialising the NIb with
Name tableViewController=[[JsonTestViewController alloc] initWithNibName:#"JsonTestViewController" bundle:nil];
In JsonTestViewController.m
I have this code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
//NSLog(#"Array: %#",rows);
return [rows count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Configure the cell.
NSDictionary *dict = [rows objectAtIndex: indexPath.row];
NSString *strlb1=[dict objectForKey:#"block"];
NSString *strlb2=[dict objectForKey:#"name"];
strlb1=[strlb1 stringByAppendingString:#" , "];
strlb1=[strlb1 stringByAppendingString:strlb2];
NSString *str1=#"FPS : ";
NSString *str2=[dict objectForKey:#"p_hours"];
NSString *strpinf;
if([str2 isEqualToString:#"FP"])
{
strpinf=#"Free Parking";
}
else if([str2 isEqualToString:#"12"])
{
strpinf=#"2 hours";
}
else if([str2 isEqualToString:#"14"])
{
strpinf=#"4 hours";
}
else if([str2 isEqualToString:#"MP"])
{
strpinf=#"Metered Parking";
}
str1=[str1 stringByAppendingString:strpinf];
cell.textLabel.text =strlb1;
cell.detailTextLabel.text = str1;
return cell;
}
- (void)viewDidLoad {
[super viewDidLoad];
NSURL *url = [NSURL URLWithString:#"SITE URL"];
NSString *jsonreturn = [[NSString alloc] initWithContentsOfURL:url];
NSData *jsonData = [jsonreturn dataUsingEncoding:NSUTF32BigEndianStringEncoding];
NSError *error = nil;
NSDictionary * dict = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:&error];
if (dict)
{
rows = [dict objectForKey:#"users"];
}
NSLog(#"Array: %#",rows);
[jsonreturn release];
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
can you give more info? This can be anything, but most likely, rows is pointing to memory where a valid array used to be. How did you create the rows array?
For example, your rows array or dictionary not longer pointing to valid memory if you created the rows array as an autoreleased object through a factory method in another method.
Here's another question that's pretty close to what you're describing:
EXC_BAD_ACCESS signal received
EDIT:
So looking at the code you provided, with these lines there are some possibilities:
NSDictionary * dict = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:&error];
if (dict) { rows = [dict objectForKey:#"users"]; }
the deserializeAsDictionary method can return either an autoreleased dictionary or NULL. so one possibility is that rows = NULL. when you try [rows count], your program will crash. Check and see what's in error, might give you some clues.
This will cause an error even when you explicitly return 2 for numberOfRowsInSection: because in cellForRowAtIndexPath:, you're still trying to access rows, even if it could possibly be NULL.
the other possibility lies in how you've defined rows. I'm guessing it's a property in your class. But where you have rows=[dict objectForKey:#"users"];, rows can point to nothing after the method's finished. Rows will still have the address of where [dict objectForKey:] was, but after the scope of the method, dict may be gone and all the data that comes with it.
NSDictionary * dict = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:&error];
under the KVC guidelines, you should expect dict to autorelease after the end of method.
and another possibility is, since i don't know the specifics of the JSON class you're using, is that when you release jsonreturn, you're also dealloc'ing all the data associated with it. So in effect, rows is pointing to nothing.
case in point, the error seems to be rooted in how you're setting/retaining/accessing rows.
try using the Build->Build&Analyze in xcode. it might give you some more hints. or throw in a bunch of NSLog(#"%d",[rows count]); all over. also try using the debugger. it'll give you a trace of method calls that lead up to [rows count] faulting.