My problem is that I have a class property that is of type NSMutableArray, as defined in my header file, yet when I attempt to modify one of the array elements (an NSDictionary) I receive the following runtime error:
2013-01-16 14:17:20.993 debtaculous[5674:c07] * Terminating app due
to uncaught exception 'NSInternalInconsistencyException', reason:
'-[__NSCFArray replaceObjectAtIndex:withObject:]: mutating method sent
to immutable object'
Header declaration:
// BudgetViewController.h
#import <UIKit/UIKit.h>
#interface BudgetViewController : UIViewController <UITableViewDataSource, UITableViewDelegate>
- (IBAction)afterTaxIncomeEditingDidEnd:(id)sender;
#property (strong, nonatomic) NSMutableArray *budgetArray;
#property (strong, nonatomic) IBOutlet UITextField *afterTaxIncome;
#property (strong, nonatomic) IBOutlet UITableView *budgetTableView;
#end
Method that generates the error:
-(void)applyCCCSWeights
{
NSMutableDictionary *valueDict;
NSString *newAmount;
for (id budgetElement in [self budgetArray]) {
valueDict = [[NSMutableDictionary alloc] initWithDictionary:budgetElement];
newAmount = [NSString stringWithFormat:#"%0.2f", [[self afterTaxIncome].text floatValue] * [[budgetElement objectForKey:#"cccs_weight"] floatValue]];
[valueDict setValue:newAmount forKeyPath:#"amount"];
[[self budgetArray] replaceObjectAtIndex:0 withObject:valueDict];
NSLog(#"%0.2f (%0.2f)", [[budgetElement objectForKey:#"amount"] floatValue], [[self afterTaxIncome].text floatValue] * [[budgetElement objectForKey:#"cccs_weight"] floatValue]);
}
[self.budgetTableView reloadData];
}
// Note the replaceObjectAtIndex:0 above is just a placeholder. This will be replaced with the correct index.
budgetArray is surely immutable, you have to create it mutable.
Probably you're doing something like this:
budgetArray= [NSArray arraWithObjects; obj1, obj2, nil];
And ignoring the compiler warning. Make it mutable:
budgetArray= [[NSMutableArray alloc]init];
I'm fairly certain you cannot change a mutable object during enumeration.
This SO question may help: Setting an object during fast enumeration problems
In your init method, put this:
budgetArray = [[NSMutableArray alloc] init];
Also, why not use dictionary and array literal syntax?
-(void)applyCCCSWeights {
NSMutableDictionary *valueDict;
NSString *newAmount;
for (NSDictionary *budgetElement in [self budgetArray]) {
valueDict = [budgetElement mutableCopy];
newAmount = [NSString stringWithFormat:#"%0.2f", [[self afterTaxIncome].text floatValue] * [budgetElement[#"cccs_weight"] floatValue]];
valueDict[#"amount"] = newAmount;
_budgetArray[0] = valueDict;
NSLog(#"%0.2f (%0.2f)", [budgetElement[#"amount"] floatValue], [[self afterTaxIncome].text floatValue] * [budgetElement[#"cccs_weight"] floatValue]);
}
[self.budgetTableView reloadData];
}
Notice that [[self budgetArray] replaceObjectAtIndex:0 withObject:valueDict];
becomes: _budgetArray[0] = valueDict;
You can't change an array while doing a fast iteration over the array. On the other hand, it's entirely unnecessary; the code is absolutely inefficient: Just make the elements of the array NSMutableDictionaries, then change the dictionaries directly, instead of creating a copy and then changing an element in the copy.
Noticed later that you use NSJSONSerialization; look at the flags and don't pass 0 blindly.
Related
With the new xcode7 Apple introduced generics and nullability to Objective-C ( Developer guide )
But it seems to be very different from what we have on swift.
Nullability:
- (nonnull NSString *)something {
return nil;
}
This should raise a warning! And you can even assign the return value of this method to a nonnull variable like:
//#property (copy, nonnull) NSString *name
obj.name = [obj something];
Generics:
Looking this example:
#property (nonatomic, strong, nonnull) NSMutableArray <UIView *> *someViews;
a warning is raised when something different from a UIView is inserted on the array
[self.someViews addObject:#"foobar"]; //<- this raises an error
but not in this case:
self.someViews = [#[#"foobar"] mutableCopy];
nor in this case:
NSString *str = [self.someViews firstObject];
So the question is, I'm using generics and nullability in a wrong way or they are far away from the Swift implementation?
self.someViews = [#[#"foobar"] mutableCopy];
mutableCopy is inherited from NSObject, where it is declared to return id. It is not declared by NSArray specifically and NSArray des not decide the return type.
NSString *str = [self.someViews firstObject];
This does give a warning for me.
I am trying to learn objective-c, and came across some crash I couldn't resolve.
I believe it's one of the basic problems, but I am new here and got lost in the middle.
I have :
ModelViewController.h
ModelViewController.m
Schedule.h
Schedule.m
in ModelViewController.h
#import <UIKit/UIKit.h>
#interface FetchScheduleVC : UIViewController
#property (copy, nonatomic) NSMutableArray *myMutableArray;
#end
in ModelViewController.m
#import "ModelViewController.h"
#import "Schedule.h"
#implementation Model
- (void)viewDidLoad {
[super viewDidLoad];
for(int i=0; i < 3; i++){
[_myMutableArray addObjects: [NSNumber numberWithInt: i]
}
}
- (IBAction)saveBtn:(UIButton *)sender {
Schedule *newSchedule = [[Schedule alloc]init];
[newSchedule createClassFromArray: _myMutableArray];
}
#end
in Schedule.h
#import <Foundation/Foundation.h>
#interface Schedule : NSObject
#property (strong, nonatomic) NSMutableArray *classArray;
-(void) createClassFromArray: (NSArray *) selectedArr;
#end
in Schedule.m
#import "Schedule.h"
#implementation Schedule
-(void) createClassFromArray: (NSArray *) selectedArr {
for(NSNumber *i in selectedArr){
NSLog(#"number in array is : %#", i);
}
}
#end
I simplified my codes a little, but the basic flow is the same.
When I run this, and click a button to call - (IBAction)saveBtn:(UIButton *)sender, I get:
-[__NSArrayI objectForKeyedSubscript:]: unrecognized selector sent to instance 0x7fcb61d2fd10
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI objectForKeyedSubscript:]: unrecognized selector sent to instance 0x7fcb61d2fd10'
Anything wrong doings in sending NSArray through Method up here?
I can only tell you what's happening in your code, not where it's happening, as you've not posted the offending code.
The objectForKeyedSubscript method is called for NSDictionary subscripting, for example:
NSDictionary *dict = #{ #"key" : #"value" };
NSString *value = dict[#"key"]; // HERE
so it looks like you are doing something like this:
for(int i=0; i < 3; i++){
[_myMutableArray addObjects: [NSNumber numberWithInt: i]
}
NSNumber *num = _myMutableArray[#"3"]; // !!!!
One more thing I can tell you, it's not to do with uninitialized array as Objective-C simply ignores attempts to dereference nil objects and this exception has gone further than that.
As you have declared myMutableArray with copy semantic as-
#property (copy, nonatomic) NSMutableArray *myMutableArray;
this will sends a copy message to the array, which results in an immutable copy.
So, to use above semantics on NSMutableArray you need to override the "setter" method as -
- (void)setArray:(NSArray *)newArray
{
if ( array != newArray )
{
[array release];
array = [newArray mutableCopy];
}
}
the above setter method just assign the reference of mutable copy of the newArray to the array, which help you to mutate the objects and thus avoiding the error.
In the codes above, I forgot to include
_myMutableArray = [[NSMutableArray alloc] init];
although i had that in my actual code.
the problem was very dumb. Sorry for this misleading question. Hope you just ignore this question cuz it's due to such a careless mistake.
In my actual code, I had duplicate variable names like this:
in Schedule.h
#import <Foundation/Foundation.h>
#interface Schedule : NSObject
#property (strong, nonatomic) NSMutableArray *selectedArr;
-(void) createClassFromArray: (NSArray *) selectedArr;
#end
..very.. careless mistake.
I have two classes - BNRItem and BNRContainer. BNRContainer is a subclass of BNRItem. In order to cut down the amount of code I paste, assume the following that I have tested and know works:
+(BNRItem * ) randomItem; // allocate and init a random item.
#property(nonatomic, readwrite, copy) NSMutableArray * subitems; // This is a property of BNRContainer class
main.m:
NSMutableArray * rand_items = [NSMutableArray alloc] init];
for (int i = 0; i < 10; i++) {
[rand_items addObject: [BNRItem randomItem]];
}
[rand_items addObject: #"HELLO"];
BNRContainer * rand_container_of_items = [BNRContainer randomItem];
rand_container_of_items.subitems = rand_items;
[rand_container_of_items.subitems addObject: #"THERE"]; // ERROR SIGABRT
NSLog(#"------------------------------------------------------");
NSLog(#"%#", rand_container_of_items);
rand_container_of_items = nil;
If I NSLog without adding #"THERE", I see "HELLO" in my description so I know that I am able to call addObject: at that point. Why do I get SIGABRT when I am trying to access ivar "subitems" of rand_container_of_items? I just can't figure this one out.
The problem seems to be the copy modifier in your declaration.
#property (nonatomic, readwrite, copy) NSMutableArray *subitems;
In the documentation, the NSCopying protocol conformance is inherited form NSArray, so my suspicion is that in this line
rand_container_of_items.subitems = rand_items;
subitems contains an immutable copy of the original array. Try removing copy from your declaration. If you need a copy use the mutableCopy method.
Problem lies here
property(nonatomic, readwrite, copy) NSMutableArray * subitems;
You should not use copy here since it will return immutable copy of the object. So that you cannot add objects to it. It could be
property(nonatomic, strong) NSMutableArray * subitems;
This line is giving sigbart as when you allocate an array to mutable array, it becomes mutable.
So, when you are copying rand_items to rand_container_of_items.subitem, it is becoming mutable.
So, to make it immutable, try following :
BNRContainer * rand_container_of_items = [BNRContainer randomItem];
rand_container_of_items.subitems = [rand_items mutablecopy];
[rand_container_of_items.subitems addObject:#"THERE"]; // ERROR SIGABRT
I have an object, as below
#import <Foundation/Foundation.h>
#interface CountriesDAO : NSObject
#property (nonatomic, retain) NSString * countryname;
#end
#import "CountriesDAO.h"
#implementation CountriesDAO
#synthesize countryname;
#end
I have saved this in Array within appDelegate.
#property (nonatomic, retain) NSArray *countriesArray;
#synthesize countriesArray;
In another controller, I fetch it like
NSArray *countriesArray = appDelegate.countriesArray; It works fine and I get array.
As I try to sort it by this way.
NSArray *countriesArray1 = appDelegate.countriesArray;
NSSortDescriptor *valueDescriptor = [[NSSortDescriptor alloc] initWithKey:#"countryname" ascending:YES];
NSArray * descriptors = [NSArray arrayWithObject:valueDescriptor];
countriesArray = [countriesArray1 sortedArrayUsingDescriptors:descriptors];
I get error
[__NSCFType count]: unrecognized selector sent to instance 0xa83b4d0
2013-11-01 13:21:08.882 ECP[13597:c07] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFType count]: unrecognized selector sent to instance 0xa83b4d0'
Here countriesArray is defined as
#property (nonatomic, retain) NSArray *countriesArray;
I did it like this and it worked for me.
countriesArray = [[NSMutableArray alloc] init];
for (CountriesDAO *info in appDelegate.countriesArray) {
[countriesArray addObject:info.countryname];
}
[countriesArray sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
You can also try...
[countriesArray1 sortUsingSelector:#selector(compare:)];
Should work
NSMutableArray *sortedArray = [appDelegate.countriesArray mutableCopy];
[sortedArray sortUsingComparator:^NSComparisonResult(CountriesDAO *object1,
CountriesDAO *object2) {
return [object1.countryname caseInsensitiveCompare:object2.countryname];
}];
NSArray *sortedCountriesArray = [sortedArray copy];
If something went wrong, check the type of object1 and object2
Its is simple Way to sort array in iOS,it's helps to you
[countriesArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [ String_1 compare: String_2];
}];
// String_1, String_2 are NSString,it's contains in countriesArray
// use NSMutableArray to rearrange and change values in countriesArray
If the array you want to sort contains NSString (which is what I get from your description) you can replace your sorting code with this:
sortedArray = [countriesArray1 sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
I know that subtracting an NSArray from NSArray if it is a single basic object found here
But what i have is an object like this
#interface Set : NSObject
#property (nonatomic, strong) NSString *ItemId;
#property (nonatomic, strong) NSString *time;
#property (nonatomic, strong) NSString *Category_id;
#property (nonatomic, strong) NSString *List_id;
#property (nonatomic, strong) NSString *name;
#end
How can i delete an array having the set object from another array with the same?
It can be done by iterations that i knw.Is there some other way ?
EDIT: For clarity
I have Array A with 5 Set objects and I have 4 Set Objects in Array B
array A and Array B contain 3 set objects with common values..[Note : memory may be different] common
All I need is an Array C =Array A - Array B that has 2 objects in resulting array C
Thank You :)
You need to implement the - (NSUInteger)hash and - (BOOL)isEqual:(id)object method in Set class.
For eg:-
- (NSUInteger)hash {
return [self.ItemId hash];
}
- (BOOL)isEqual:(id)object
{
return ([object isKindOfClass:[self class]] &&
[[object ItemId] isEqual:_ItemId])
}
After that try this:
NSMutableSet *set1 = [NSMutableSet setWithArray:array1];
NSMutableSet *set2 = [NSMutableSet setWithArray:array2];
[set1 intersectSet:set2]; //this will give you only the obejcts that are in both sets
NSArray *commonItems = [set1 allObjects];
[mutableArray1 removeObjectsInArray:commonItems];//mutableArray1 is the mutable copy of array1
mutableArray1 will have all objects in the same order as earlier after removing common objects.
By using NSSet and NSPredicate we can meet your requirement.
Assessors *ass1 = [[Assessors alloc] init];
ass1.AssessorID = #"3";
Assessors *ass2 = [[Assessors alloc] init];
ass2.AssessorID = #"2";
Assessors *ass3 = [[Assessors alloc] init];
ass3.AssessorID = #"1";
Assessors *ass4 = [[Assessors alloc] init];
ass4.AssessorID = #"2";
NSSet *nsset1 = [NSSet setWithObjects:ass1, ass2, nil];
NSSet *nsset2 = [NSSet setWithObjects:ass3, ass4, nil];
// retrieve the IDs of the objects in nsset2
NSSet *nsset2_ids = [nsset2 valueForKey:#"AssessorID"];
// only keep the objects of nsset1 whose 'id' are not in nsset2_ids
NSSet *nsset1_minus_nsset2 = [nsset1 filteredSetUsingPredicate:[NSPredicate predicateWithFormat:#"NOT AssessorID IN %#",nsset2_ids]];
for(Assessors *a in nsset1_minus_nsset2)
NSLog(#"Unique ID : %#",a.AssessorID);
Here Assessors is my NSObject Class (Set in your case) and AssessorID is one property of that class.
Hope this can help.