mutating method sent to immutable object error when using mutable array functions - objective-c

My application always popping 'mutating method sent to immutable object'
My dictionary and the array already declare to be a mutable one but seems it doesn't help
I have tried the replaceObjectAtIndex function it also didn't work.
NSString* plistpath = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"];
NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithContentsOfFile:plistpath];
NSMutableArray* Array1 = [dictionary objectForKey:#"Array1"];
[Array1 removeObjectAtIndex:num];
[Array1 insertObject:#"1" atIndex:num];
return Array1;
thanks

Try:
NSArray *array = [dictionary objectForKey:#"Array1"]; // Immutable
NSMutableArray * Array1 = [NSMutableArray arrayWithArray:array]; // Mutable
// ...
Or as Julien noted:
NSArray *array = [dictionary objectForKey:#"Array1"]; // Immutable
NSMutableArray * Array1 = [array mutableCopy]; // Mutable

If the right hand side does not return an NSMutableArray (NSArray instead) you try to mutate an in mutable object.
NSMutableArray* Array1 = [dictionary objectForKey:#"Array1"]; // NSArray

Related

Setting a value for a key in a nested NSDictionary error (immutable)

So I've got this code:
self.items is an NSMutableArray
[self.items enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSDictionary *checkin, NSUInteger index, BOOL *stop) {
if ([checkin[#"_id"] isEqualToString:checkinID]) {
NSMutableDictionary *checkin = (NSMutableDictionary *)self.items[index];
NSMutableDictionary *user_data = (NSMutableDictionary *)checkin[#"user_data"];
user_data[#"likes_checkin"] = #1;
checkin[#"user_data"] = user_data;
self.items[index] = checkin;
}
}];
But receive the error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
I'm not sure how this error is occurring?
Is the approach I'm taking to do this the best way? It seems very verbose...
You seem to be casting immutable dictionaries to mutable dictionaries:
NSMutableDictionary *checkin = (NSMutableDictionary *)self.items[index];
That will not work. A cast will not actually alter the object and change it from an NSDictionary to NSMutableDictionary. This would:
NSMutableDictionary *checkin = [self.items[index] mutableCopy];
mutableCopy, when available, will make a new copy of the object that is mutable. Calling mutableCopy on an NSDictionary will result in a new NSMutableDictionary with the original dictionary's objects and keys.
You are modifying the array while you are enumerating it and you are attempting to modify an immutable dictionary, not a mutable one.
If you are looking to modify just one dictionary in the array then first find the index of the dictionary you want to change and then change the dictionary outside of the enumeration:
__block NSUInteger foundIndex = NSNotFound;
[self.items enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(NSDictionary *checkin, NSUInteger index, BOOL *stop) {
if ([checkin[#"_id"] isEqualToString:checkinID]) {
foundIndex = index;
*stop = YES;
}
}];
if (foundIndex != NSNotFound) {
NSMutableDictionary *checkin = [self.items[foundIndex] mutableCopy];
NSMutableDictionary *user_data = [checkin[#"user_data"] mutableCopy];
user_data[#"likes_checkin"] = #1;
checkin[#"user_data"] = user_data;
self.items[foundIndex] = checkin;
}
self.items is a NSMutableArray object but an item is immutable NSDictionary. You are adding an NSDictionary object inside self.items. Therefore add an NSMutableDictionary object to get immutability.
You can do so:
NSDictionary *yourImmutableDictionary = [NSDictionary doSomething];
NSMutableDictionary *yourMutableDictionary = [NSMutableDictionary dictionaryWithDictionary:yourImmutableDictionary];
or
NSMutableDictionary *yourMutableDictionary = [yourImmutableDictionary mutableCopy];

Add NSStrings to mutable array

I created a mutable array and I have two NSString variables. Now I want to add these two NSStrings to my array. How is this possible? Thanks.
Use the addObject function of you NSMutableArray.
eg.
[myNSMutableArray addObject:myString1];
[myNSMutableArray addObject:myString2];
Jhaliya's answer is correct. +1 vote.
I added a immutable version so you can see the difference. If you dont want to remove or add more objects (NSStrings) to your container, I would recommend using an Immutable version.
Mutable version:
NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
NSString *string_one = #"One"];
[mutableArray addObject:string_one];
//Or
[mutableArray addObject:#"Two"];
NSLog(#"%#", mutableArray);
Immutable version
NSArray *immutableArray = [NSArray arrayWithObjects:#"One", #"Two", nil];
NSLog(#"%#", immutableArray);
You can add at NSMutableArray allocation.
Like :
NSMutableArray *test = [NSMutableArray arrayWithObjects:#"test1",#"test2",nil];

How to return an NSMutableArray from an NSSet

I'm able to put the contents of an NSSet into an NSMutableArray like this:
NSMutableArray *array = [set allObjects];
The compiler complains though because [set allObjects] returns an NSArray not an NSMutableArray. How should this be fixed?
Since -allObjects returns an array, you can create a mutable version with:
NSMutableArray *array = [NSMutableArray arrayWithArray:[set allObjects]];
Or, alternatively, if you want to handle the object ownership:
NSMutableArray *array = [[set allObjects] mutableCopy];
I resolved crashing by using NSMutableArray's method 'addObjectsFromArray' to assign all NSSet objects to NSMutableArray like:
[mutableArray addObjectsFromArray:[cg_Schedule.schedule_Days allObjects]];
Hope this will helps you.
For an ordered set use:
NSArray *myArray = [[myOrderedSet array] mutableCopy];

How add data from NSMutableString into NSArray?

is it an possible to add a value from an NSMutableString into an NSArray? Whats the snippet?
Actually, Mike is wrong. If you want to instantiate an NSArray with a single NSMutableString object, you can do the following:
NSMutableString *myString; //Assuming your string is here
NSArray *array = [NSArray arrayWithObject:myString];
There is no arrayWithElements in NSArray (see NSArray documentation)
If you want to instantiate an NSArray with a single NSMutableString object, you can do the following:
NSString *myString; //Assuming your string is here
NSArray *array = [NSArray arrayWithObjects:myString,nil];
Note that NSArray will be immutable - that is, you can't add or remove objects to it after you've made it. If you want the array to be mutable, you'll have to create an NSMutableArray. To use an NSMutableArray in this fashion, you can do the following:
NSString *myString; //Assuming your string is here
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:myString];
NSArray is immutable, so you cannot add values to it. You should use NSMutableArray in order to do that with the addObject: method.
NSMutableString *str = ...
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:str];
// You must use NSMutableArray to add Object to array
NSMutableArray *tableCellNames;
// arrayWithCapacity is a required parameter to define limit of your object.
tableCellNames = [NSMutableArray arrayWithCapacity:total_rows];
[tableCellNames addObject:title];
NSLog(#"Array table cell %#",tableCellNames);
//Thanks VKJ
An elegant solution would be this:
NSMutableString *str; //your string here
NSArray *newArray = #[str];
Using the new notation, it's a piece of cake.

How do I convert NSMutableArray to NSArray?

How do I convert NSMutableArray to NSArray in objective-c?
NSArray *array = [mutableArray copy];
Copy makes immutable copies. This is quite useful because Apple can make various optimizations. For example sending copy to a immutable array only retains the object and returns self.
If you don't use garbage collection or ARC remember that -copy retains the object.
An NSMutableArray is a subclass of NSArray so you won't always need to convert but if you want to make sure that the array can't be modified you can create a NSArray either of these ways depending on whether you want it autoreleased or not:
/* Not autoreleased */
NSArray *array = [[NSArray alloc] initWithArray:mutableArray];
/* Autoreleased array */
NSArray *array = [NSArray arrayWithArray:mutableArray];
EDIT: The solution provided by Georg Schölly is a better way of doing it and a lot cleaner, especially now that we have ARC and don't even have to call autorelease.
I like both of the 2 main solutions:
NSArray *array = [NSArray arrayWithArray:mutableArray];
Or
NSArray *array = [mutableArray copy];
The primary difference I see in them is how they behave when mutableArray is nil:
NSMutableArray *mutableArray = nil;
NSArray *array = [NSArray arrayWithArray:mutableArray];
// array == #[] (empty array)
NSMutableArray *mutableArray = nil;
NSArray *array = [mutableArray copy];
// array == nil
you try this code---
NSMutableArray *myMutableArray = [myArray mutableCopy];
and
NSArray *myArray = [myMutableArray copy];
Objective-C
Below is way to convert NSMutableArray to NSArray:
//oldArray is having NSMutableArray data-type.
//Using Init with Array method.
NSArray *newArray1 = [[NSArray alloc]initWithArray:oldArray];
//Make copy of array
NSArray *newArray2 = [oldArray copy];
//Make mutablecopy of array
NSArray *newArray3 = [oldArray mutableCopy];
//Directly stored NSMutableArray to NSArray.
NSArray *newArray4 = oldArray;
Swift
In Swift 3.0 there is new data type Array. Declare Array using let keyword then it would become NSArray And if declare using var keyword then it's become NSMutableArray.
Sample code:
let newArray = oldArray as Array
In objective-c :
NSArray *myArray = [myMutableArray copy];
In swift :
var arr = myMutableArray as NSArray
NSArray *array = mutableArray;
This [mutableArray copy] antipattern is all over sample code. Stop doing so for throwaway mutable arrays that are transient and get deallocated at the end of the current scope.
There is no way the runtime could optimize out the wasteful copying of a mutable array that is just about to go out of scope, decrefed to 0 and deallocated for good.
If you're constructing an array via mutability and then want to return an immutable version, you can simply return the mutable array as an "NSArray" via inheritance.
- (NSArray *)arrayOfStrings {
NSMutableArray *mutableArray = [NSMutableArray array];
mutableArray[0] = #"foo";
mutableArray[1] = #"bar";
return mutableArray;
}
If you "trust" the caller to treat the (technically still mutable) return object as an immutable NSArray, this is a cheaper option than [mutableArray copy].
Apple concurs:
To determine whether it can change a received object, the receiver of a message must rely on the formal type of the return value. If it receives, for example, an array object typed as immutable, it should not attempt to mutate it. It is not an acceptable programming practice to determine if an object is mutable based on its class membership.
The above practice is discussed in more detail here:
Best Practice: Return mutableArray.copy or mutableArray if return type is NSArray
i was search for the answer in swift 3 and this question was showed as first result in search and i get inspired the answer from it
so here is the swift 3 code
let array: [String] = nsMutableArrayObject.copy() as! [String]