Mutating method sent to immutable object with NSMutableArray - ios

I have found the Error Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object' when i add object to mutable array at second time first time added successfully but second time it is crash the app i think there is problem with when i added object to array.
Below code is crash.
- (void) viewWillAppear:(BOOL)animated
{
if ([defaults objectForKey:#"Groups"] != nil)
{
NSLog(#"not nil defaults.");
arrGroups = (NSMutableArray *)[defaults objectForKey:#"Groups"];
}
else
{
NSLog(#"nil defaults.");
arrGroups = [[NSMutableArray alloc] init];
}
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSLog(#"button index == %ld",(long)buttonIndex);
//txtCategory = [[alertView textFieldAtIndex:0] text];
if (buttonIndex == 1)
{
//[self addingCategory:self];
NSLog(#"Adding Group name = %#",[[alertView textFieldAtIndex:0]text]);
[arrGroups addObject:[[alertView textFieldAtIndex:0]text]]; **//Crash here! at the time of add second object or also when i remove first object**
NSLog(#"Added to array.");
[defaults setObject:arrGroups forKey:#"Groups"];
[defaults synchronize];
//[defaults release];
[tblGroups reloadData];
}
}
when i remove first object at that time i replace the userdefault with updated array so there is no problem i think. and i did not found proper reason for that crash.
so please support me either understood me the problem or solution but without understand the problem i can't understood solution so please any one tell that why this happen.
Thanks.

The problem with assigning to a NSMutableArray is, it will only work if defaultDefects was assigned an NSMutableArray for the given key.
Note: NSUserDefaults always returns an immutable object.
Do this instead
NSMutableArray *arrGroups = [[defaults objectForKey:#"Groups"]mutableCopy];
this guarantees a mutable copy.
Another way is.
arrGroups = [[NSMutableArray alloc]initWithArray:[defaults objectForKey:#"Groups"]]; //in your viewWillAppear where you assign array from defaults.

Related

<__NSCFDictionary: 0x1557f400> was mutated while being enumerated.'

I have ios 7 application that is running on iphone 4. I have a weird problem, where application crashes inside for loop, because of the error in the title. I checked on SO and it says that error occurs when you change object over which you are iterating.
So I copied both variables that I use to temp variables but problem still occurs.
Problem happen when first iteration is finished.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary * badges = [defaults objectForKey:#"badges"];
NSMutableDictionary *newBadges = badges;
for(NSString* key in badges)
{
NSDictionary* badge = [badges objectForKey:key];
if([[badge objectForKey:#"achived"] isEqual: #"NO"])
{
if([self checkBadgeCondition:badge])
{
NSMutableDictionary *tempBadge = [badge mutableCopy];
[self showAlertBadge:badge];
[tempBadge setObject:#"YES" forKey:#"achived"];
[newBadges setObject:tempBadge forKey:[tempBadge objectForKey:#"name"]];
}
}
}
newBadges = badges
This isn't a copy, it's just another reference to the same thing. You also should expect a dictionary (or array) coming out of user defaults to be mutable. So, make a mutable copy of it here
newBadges = [badges mutableCopy]

iOS - get NSDictionary from NSUserDefaults :Attempted to dereference an invalid ObjC Object or send it an unrecognized selector

I try to retrieve NSMutableArray from NSUserDefaults that have been saved.
I store the NSMutableArray:
NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
NSMutableArray* mySavedTremps = [[defaults objectForKey:UD_MY_TREMPS] mutableCopy];
if (!mySavedTremps)
mySavedTremps =[[NSMutableArray alloc] init];
NSMutableDictionary* trempDict = NSMutableDictionary* trempDict = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"please", #"help", #"me" #"!", nil]
[trempDict setValue:trempId forKey:#"trempId"];
[mySavedTremps insertObject:trempDict atIndex:0];
[defaults setObject:mySavedTremps forKey:UD_MY_TREMPS];
[defaults synchronize];
And try to retrieve the NSMutableArray:
NSMutableArray* myTrempsArray = [NSMutableArray arrayWithArray:[defaults objectForKey:UD_MY_TREMPS]];
for (Tremp* tremp in myTrempsArray) {
if([tremp.trempId isEqualToString:#"1234"]) {
[myTrempsArray removeObject:tremp];
break;
}
}
But, when I access tremp (param in the for loop) like:
tremp.trempId
I get this error:
error: Execution was interrupted, reason: Attempted to dereference an invalid ObjC Object or send it an unrecognized selector.
The process has been returned to the state before expression evaluation.
When you save your Tremp object to the defaults, you are actually saving it as a dictionary.
But when you read it out, your code assumes you have an array of Tremp objects.
You want something like:
for (NSDictionary *trempDict in myTrempsArray) {
Tremp *tremp = ... // add code here to create a Tremp from the dictionary
if([tremp.trempId isEqualToString:#"1234"]) {
[myTrempsArray removeObject:tremp];
break;
}
}
BTW - this code will crash. You can't modify an array that you are fast-enumerating through. Change the loop to be a standard for loop but go through the loop in reverse.
Also, when you save the data, replace the call to setValue:forKey: to setObject:forKey:.

iOS Terminating app due to uncaught exception 'NSUnknownKeyException'

I am pretty new to objective c and iOS programming, and I have this pretty strange error. The app in question initializes a NSMutableArray with a preset set of values of a custom type I made using NSObject. Which is manipulated by the app. If new values are added during app run time, they are saved using NSUserDefaults, and are brought up from NSUserDefaults along with the default values on next app open.
This is the error I get:
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<__NSCFConstantString 0xb404> valueForUndefinedKey:]: this class is not key value coding-compliant for the key score.'
*** First throw call stack:
(0x1c9b012 0x10d8e7e 0x1d23fb1 0xb84d1d 0xaf100b 0xaf0fbd 0xb0f247 0xb3023c 0xb30056 0x3e40 0x3c5f 0x11f5ad 0x10ec705 0x202c0 0x20258 0x242ff4 0x10ec705 0x202c0 0x20258 0xe1021 0xe157f 0xe1056 0x246af9 0x10ec705 0x202c0 0x20258 0xe1021 0xe157f 0xe06e8 0x4fcef 0x4ff02 0x2dd4a 0x1f698 0x1bf6df9 0x1bf6ad0 0x1c10bf5 0x1c10962 0x1c41bb6 0x1c40f44 0x1c40e1b 0x1bf57e3 0x1bf5668 0x1cffc 0x290d 0x2835)
libc++abi.dylib: terminate called throwing an exception
I am not quite sure what the error is or how to go about debugging it.
Previously this code worked flawlessly, all I did was remove one or two elements from the preset default list of elements, and in the simulator, simulated deleting the app, and recompiled the code. Ever since my program crashes, with the above message, and I can't figure out how to fix it.
So if someone can give me some help on how to go about debugging this, that would be wonderful. I can attach code as needed, i'm not sure what code would be relevant to be shown, and it may be too much to post all the code involved in the project.
Code to encode and decode the properties of my custom Name NSObject class called name.h:
-(void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeInteger:self.score forKey:#"score"];
}
-(id)initWithCoder:(NSCoder *)decoder
{
if((self = [super init]))
{
self.name = [decoder decodeObjectForKey:#"name"];
self.score = [decoder decodeIntegerForKey:#"score"];
}
return self;
}
Retrieving Data from class, incase this matters, this code occurs in appdelegate.m:
NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:#"dataArray"];
NSInteger score = 0;
NSMutableArray *temp = [[NSMutableArray alloc] initWithObjects:#"name", nil];
NSMutableArray *tempList = [[NSMutableArray alloc] init];
for(NSString *y in temp)
{
Name *name = [[Name alloc] init];
name.name = y;
name.score = score;
[tempList addObject:name];
}
if (data)
{
NSArray *list = [NSKeyedUnarchiver unarchiveObjectWithData:data];
NSMutableArray *names = [[NSMutableArray alloc]initWithArray:list];
// [_nameList addObjectsFromArray:temp];
NSMutableArray *t = [[names arrayByAddingObjectsFromArray:tempList ] mutableCopy];
_nameList = [[NSMutableArray alloc]init];
[_nameList addObjectsFromArray:t];
}
else
{
//First time load or data is not saved yet
_nameList = [[NSMutableArray alloc] initWithObjects:#"name", nil];
}
saving the array at close time:
- (void)applicationWillTerminate:(UIApplication *)application
{
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
NSData *data =[NSKeyedArchiver archivedDataWithRootObject:_nameList];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"dataArray"];
}
same code is in applicationDidEnterBackground.
code to sort by 'score'
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController: (UIViewController *)viewController
{
if(viewController == _viewController3)
{
[self sortNames:_nameList];
[[(ThirdViewController*)_viewController3 topList] reloadData];
}
}
-(void)sortNames:(NSMutableArray*)test
{
NSArray* temp = [[NSArray alloc] initWithArray:test];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"score" ascending:NO];
NSArray *sortedLinks = [[temp sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]] mutableCopy];
_nameList = (NSMutableArray*) sortedLinks;
}
Here's my two cents. You have this line:
[encoder encodeObject:self.name forKey:#"name"];
And then this line:
Name *name = [[Name alloc] init];
This makes me think that the "self.name" property is one of these "Name" custom subclasses.
I believe that if you make a custom subclass and you want it to encodewithcoder, you have to add the encodewithcoder method to your custom subclass and have it encodewithcoder all of its properties and instance variables as primitively as you can.
Meaning, your Name class needs to have its own encodewithcoder method that encodes all of its properties and instance variables that have been stored as factory objects or c primitives.
Edit: I'm still pretty new and value my rep. If I'm wrong, please comment and I'll delete but please don't downvote me to oblivion
Here:
_nameList = [[NSMutableArray alloc] initWithObjects:#"name", nil];
you are adding an NSString to the _nameList array. Later you save that array.
The next time you load that array, you have the NSString #"name" in it. I guess at some point you iterate through the items in _nameList and try to get or set the score, since you are calling this on a subclass of NSString you get the NSUnknownKeyException.
I think what you want to is to replace the line above with something like this: (assuming the class in your name.h file you mentioned above is called Name)
Name *newName = [[Name alloc] init]; //or initialize the way you need to
_nameList = [[NSMutableArray alloc] initWithObjects:newName, nil];

[__NSCFString count]: unrecognized selector sent to instance 0x6a7a1a0 while taking datas from json string

- (void)viewDidLoad
{
binding.logXMLInOut = YES; // to get logging to the console.
StationDetailsJsonSvc_getAvailableStations *request = [[StationDetailsJsonSvc_getAvailableStations new] autorelease];
request.userName=#"twinkle";
request.password=#"twinkle";
StationDetailsJsonSoap11BindingResponse *resp = [binding getAvailableStationsUsingParameters:request];
for (id mine in resp.bodyParts)
{
if ([mine isKindOfClass:[StationDetailsJsonSvc_getAvailableStationsResponse class]])
{
resultsring = [mine return_];
NSLog(#"list string is%#",resultsring);
}
}
#pragma mark parsing
SBJsonParser *parserq = [[SBJsonParser alloc] init];
//if successful, i can have a look inside parsedJSON - its worked as an NSdictionary and NSArray
results= [parserq objectWithString:resultsring error:nil];
NSLog(#"print %#",results);
for (status in results)
{
NSLog(#"%# ",[status objectForKey:#"1" ]);
events=[status objectForKey:#"1"];
NSLog(#"get%#",events);
NSLog(#"events%#",events);
}
events=[status objectForKey:#"1"];
NSLog(#"post%#",events);
self.navigationController.navigationBarHidden=YES;
[whethertableview reloadData];
[super viewDidLoad];
}
this is my code am not getting tableview contents if i run my app crashes getting [NSCFString count]:unrecognized selector sent to instance
You should not get count on NSString but on arrays
you should call [yourString length] to check if the string has something.
You are trying to get the count of a string , which is crashing the App
There are various improvements you could make with this code, but I think I see the problem:
As you are not using ARC, you need to retain what you take out of the parser:
So instead of:
events=[status objectForKey:#"1"]
You need to do:
events= [[status objectForKey:#"1"] retain];
Your crash is caused by accessing a variable that has already been released. More than likely it is the events variable.
...and to add to this. events is probably an NSArray which 'count' is being called on. And [status objectForKey:#"1"] is returning a string... there are many possibilities which i'm speculating about. If events is an NSArray, this isn't the way to add objects to the array.. you repeatedly call events=[status objectForKey:#"1"]; in a loop too.

attempt to insert nil object from objects[0]?

In my application I have a UITableView and some buttons that the user can click to sort the array to the order based upon some NSDate's or ints. So this is my method to try to sort my UITableView:
- (void)sortStats:(id)sender reloadData:(BOOL)dataBool {
NSSortDescriptor *descriptor = nil;
UIButton *clickedButton = (UIButton*)sender;
if (clickedButton.tag == 1) {
descriptor = [NSSortDescriptor sortDescriptorWithKey:#"Date" ascending:NO];
}
[self.cellArray sortUsingDescriptors:[NSArray arrayWithObject:descriptor]];
[[NSUserDefaults standardUserDefaults] setObject:self.cellArray forKey:#"cellArray"];
if (dataBool) {
[ivstatstableView reloadData];
}
[ivstatstableView scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
}
So originally I just had this method without the reloadData parameter because it seemed that reloadData on the UITableView was causing the crash.
This is what the console crash log is:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0]'
Anyway is there any code here that would be causing this crash? I really want to get this functionality working in my app and I know I am close to fixing it but I am just not sure whats causing this issue.
Any ideas?
There is at least one place where this could be happening, and perhaps others. In the code where you get the contents of the user defaults, you don't check for nil. So instead of this:
[self.cellArray addObjectsFromArray:[[NSUserDefaults standardUserDefaults] objectForKey:#"array"]];
you might want to try this:
NSArray *defaultCellArray = [[NSUserDefaults standardUserDefaults] objectForKey:#"array"];
if (defaultCellArray) {
[self.cellArray addObjectsFromArray:defaultCellArray];
}
It is also possible that the initWithObjects call that is failing (according to your error message) is nested within another call, perhaps in this call:
[self.cellArray sortUsingDescriptors:[NSArray arrayWithObject:descriptor]];
If you look at the full call stack it would tell you if this call is making subsequent calls to initWithObjects and possibly passing it a nil. In your case, for example, you would pass in a nil yo the array you are creating if clickedButton.tag has not yet been set to a value of 1 or 2. You might want to add an additional else clause to help diagnose the problem:
if (clickedButton.tag == 1) {
descriptor = [NSSortDescriptor sortDescriptorWithKey:#"Date" ascending:NO];
} else if (clickedButton.tag == 2) {
descriptor = [NSSortDescriptor sortDescriptorWithKey:#"Score" ascending:NO];
} else {
NSLog(#"Unexpected error: Can't determine sort descriptor");
}
This is all pretty complicated, but one thing sticks out:
[[NSUserDefaults standardUserDefaults] setObject:self.cellArray
forKey:#"cellArray"];
I think you should change that to this:
NSArray *array = [NSArray arrayWithArray:self.cellArray];
[[NSUserDefaults standardUserDefaults] setObject:array forKey:#"cellArray"];
Secondly, add asserts() after every place where you think you should have an object, i.e.:
UIButton *theButton = ...
assert(theButton);
You also say that the number of rows is double the cellArray count, guess thats intentional.

Resources