Saving an NSMutableDictionary to NSUserDefaults - ios

In my application one of the things I am saving is the value of a group of switches. The idea is to save the state of each switch in an NSMutableDictionary. In another method I read out the value ("state") of each switch and use that to perform a simple calculation. These switches tell me what the user has, basically.
In my data model.
-(void)savePlates:(NSMutableDictionary *)settingPlates {
[self setObject:settingPlates forKey:#"plates"];
[self synchronize];
}
-(NSMutableArray *)loadPlates {
NSMutableArray *plates = [[self dictionaryForKey:#"plates"] mutableCopy];
return plates;
}
An example of using this in the View Controller. Where 'settings' is an instance of the class that defines the methods above.
-(IBAction)switch55:(id)sender {
if (self.switch55.on) {
[self.plateSelectionDict setObject:#YES forKey:#55];
} else {
[self.plateSelectionDict setObject:#NO forKey:#55];
}
[settings savePlates:self.plateSelectionDict];
if (DEBUG) {
NSLog(#"%#", self.plateSelectionDict);
}
}
I am crashing on this line.
[self setObject:settingPlates forKey:#"plates"];
The error is,
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'*** -[Settings setObject:forKey:]: attempt to insert non-property list object {
55 = 0;
25 = 1;
"0.25" = 1;
"0.5" = 1;
"0.75" = 1;
45 = 1;
15 = 0;
1 = 1;
10 = 1;
"2.5" = 1;
35 = 1;
5 = 1;
} for key plates'
What is confusing is that it is spitting out the correct values, but still throwing an exception. I must be doing something terribly wrong. Any assistance would be appreciated.

All keys must be strings if you need to save it to NSUserDefaults or a plist. Your keys are numbers.

To save objects to user defaults , your objects need to be valid property list objects.
If you want to check which objects are valid property list objects, just create a new plist file and play for a while with it , add some entries and you'll see what types are already handled / available to be saved without any previous preprocessing to read/write them.
At first glance it seems your values are booleans and string / nsnumbers keys, which are valid.
I'd suggest printing out the class of the objects and checking if all them are actually valid.
You can get more information here
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/PropertyLists/Introduction/Introduction.html

Related

__NSCFDictionary doesn't respond to any selectors

I am trying to Parse JSON that looks like this:
food = {
"food_id" = 4823;
servings = {
serving = (
{
calcium = 9;
calories = 221;
carbohydrate = "16.20";
cholesterol = 31;
I can successfully retrieve the array at the [food][servings][serving] level, which I confirm via a log statement has the appropriate class, __NSCFArray, but I run into a problem when iterating through that array and trying to do useful things with the contained information:
for (id foodId in resultsPerServing) {
NSLog(#"hree is a result %#", foodId);
foodObjectClass *foodObject = [foodObjectClass new];
NSDictionary *foodIdDictionary = (NSDictionary *)foodId;
if ([foodIdDictionary respondsToSelector:#selector(allKeys)]) {
[foodObject getDetailsFromResponseObject:foodIdDictionary];
} else {
unsigned int mc = 0;
Method * mlist = class_copyMethodList(object_getClass(foodIdDictionary), &mc);
NSLog(#"%d methods", mc);
for(int i=0;i<mc;i++)
NSLog(#"Method no #%d: %s", i, sel_getName(method_getName(mlist[i])));
[NSException raise:#"DIDN'T GET A DICTIONARY" format:#"here is the object %# which has class %#", resultsPerServing, [responseObject class]];
}
}
This causes the NSException to be raised without fail although in the NSException the output from printing the object looks like a dictionary. Additionally, when the exception prints out the class of the object which does not respond to allKeys, that object has class __NSCFDictionary. What gives? Why does this dictionary not have an allKeys method and how can I get a functional dictionary to work with?
Even more puzzling is that the runtime code below (copied from an SO post for which I have lost the link) indicates that the dictionary has 0 methods. Why does this object have no methods? How can I get an object that does have methods?
make a check to confirm that it as NSDictionary first?
if ([foodId isKindOfClass:[NSDictionary class]] ) {
foodIdDictionary = (NSDictionary *)foodId;
[foodObject getDetailsFromResponseObject:foodIdDictionary];
}

How To convert [__NSArrayI integerValue] to integer value?

This the line i have using to convert the object to integer values,Inside For Loop I have Placed This code
NSInteger tag=[[arrFullSubCategory valueForKey:#"category"] integerValue];
Inside arrFullSubCategory:
(
{
category = 35;
image = "images/Hatchback.jpg";
name = Hatchback;
parent = 20;
},
{
category = 36;
image = "images/Sedan.jpg";
name = Sedan;
parent = 20;
},
{
category = 37;
image = "images/SUV.jpg";
name = SUV;
parent = 20;
}
)
Exception:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSArrayI integerValue]: unrecognized selector sent to instance 0x7ff4ba58f930'
arrFullSubCategory is an array and you should reach it's elements first. Than you will have NSDictionary objects. After that you can access your category element. So I think your code should be like that:
for (NSInteger i = 0; i < arrFullSubCategory.count; ++i) {
NSInteger tag=[[[arrFullSubCategory objectAtIndex:i] valueForKey:#"category"] integerValue];
}
Explanation of the error:
The error means you have an array, and arrays don't respond to integerValue.
Your variable arrFullSubCategory references an array (of 3 elements), and each element is a dictionary. If you call valueForKey: on an array of dictionaries then the key lookup is performed for each dictionary and an array is constructed for the results. In your case the result (using literal syntax) is the array:
#[ #35, #36, #37 ]
Whether this array is directly useful to you, or whether you should access the array one element at a time - using a loop or method which calls a block per element, etc. - will depend on what your goal is.
HTH
Try this code inside for loop I hope this help you
NSInteger tag=[[[arrFullSubCategory objectAtIndex:i] valueForKey:#"category"] integerValue];
you have array of dictionary, So you use it given below code
[[[arrFullSubCategory objectAtIndex:] objectForKey:#"category"] integerValue]

Two objects pointing to same address is not working. e.g. newArray = oldArray

I am using the following type of object reference copy functionality throughout my iOS application.
e.g. objectA = objectB;
When you perform operation/changes on objectA it would automatically get reflected in objectB. You do not need to copy objectA to objectB again to reflect the changes in objectB, as they are pointing/referring to same location(address).
The problem is it was working fine till the iOS 8.2 however it seems to not work in iOS 8.3 at all.
Following is one of the code snippet which is not working correctly.
NSMutableDictionary *fieldObj = self.theData[indexPath.section ][indexPath.row];
// Adding text to data array
[fieldObj setObject:textField.text.stringByStrippingHTML forKey:#"value"];
NSLog(#"Line 1 \n%#",fieldObj);
NSLog(#"Line 2 \n%#",self.theData[indexPath.section][indexPath.row]);
From Above code Line 1 and Line 2 are giving same output upto iOS 8.2.
Line 1
{
name = Name;
required = NO;
type = 4;
value = asdfasdfasdfasdfsad;
}
Line 2
{
name = Name;
required = NO;
type = 4;
value = asdfasdfasdfasdfsad;
}
however in iOS 8.3 they are giving the different outputs.
Line 1
{
name = Name;
required = NO;
type = 4;
value = asdfasdfasdfasdfsad;
}
Line 2
{
name = Name;
required = NO;
type = 4;
value = "";
}
Am I doing anything wrong here?
If not does anyone know about the issue and how to solve it?
EDIT:
Sorry for the above misleading question, the reason behind above problem I found is that the indexPathForItemAtPoint:(CGPoint) is returning wrong indexPath in iOS 8.3 only.
The code I have used is as follow:
// retrieve indexpath from uitextfield
CGPoint pos = [textField convertPoint:CGPointZero toView:self.collectionView];
NSIndexPath *indexPath = [self.collectionView indexPathForItemAtPoint:pos];
The above code is working fine upto iOS versions 8.2.
I made and easy snippet that I think it will reflect you actual situation: an immutable container with mutable container inside.
int main(int argc, const char * argv[]) {
#autoreleasepool {
// insert code here...
NSLog(#"Hello, World!");
NSArray * array = #[#{#"KEY" : #"OLD" }.mutableCopy];
NSMutableDictionary * dict = array.firstObject;
[dict setObject:#"NEW" forKey:#"KEY"];
NSLog(#"Mutated dict %#",dict);
NSLog(#"From original source %#",array.firstObject);
}
return 0;
}
Everything seems to work as expected, are you sure that you are doing something somewhere else?
2015-04-15 08:37:09.740 prova[912:117578] Mutated dict {
KEY = NEW; } 2015-04-15 08:37:09.741 prova[912:117578] From original source {
KEY = NEW; }
Also the both object have the same address as expected:
(lldb) expression dict (__NSDictionaryM *) $2 = 0x00000001003004a0 1
key/value pair (lldb) expression array.firstObject (__NSDictionaryM *)
$3 = 0x00000001003004a0 1 key/value pair
Try to set it as below.
NSMutableDictionary *fieldObj =
[
self.theData[indexPath.section ][indexPath.row]
mutableCopy
];
& check. I believe it will work.

How to avoid NSMutable array add object values changes before adding object to NSMutable array?

I am new to Objective C. I had used following code.
for (int i = 0; i < [ValueisFound count]; i++)
{
NSString* ObjectName = [NSString stringWithUTF8String:name];
ObjectName = [[NSClassFromString([NSString stringWithUTF8String:samType]) alloc]init];
NSMutableDictionary* jsonDictionary=[TemplateClass SeparateArray:jsonValue_1 key:[arrayofKeys objectAtIndex:j] index:i];
ObjectName = [TemplateClass JsonPharser:str1 jsonObject:jsonDictionary];
NSMutableArray *samArray=[[NSMutableArray alloc]initWithObjects:[TemplateClass JsonPharser:str1 jsonObject:jsonDictionary], nil];
[manoj_Array addObject:samArray];
[samArray release];
[ObjectName release];
}
While executing for loop:
at i=0, Object has value(number=10), now manoj_Array also has (number=10).
at i=1, Object has value(number=12), now manoj_Array has (number=12,number=12).
But i want the result as manoj_Array has (number=10,number=12). I don't know how that array values are changing to last value.
My mind
samArray and manoj_Array is shared the memory Reference Thats why last value is insert to the manoj_Array
i = 0 value is Store by the address of 1000
i = 1 value is Store by the address of 1002
but manoj_Array share the memory so change the Last value is Added to manoj_Array
You can implement KVO in order to know when a value has changed and handle its behaviour.

How for in loop works internally - Objective C - Foundation

I found this answer:
https://stackoverflow.com/a/5163334/1364174
Which presents how for in loop is implemented.
NSFastEnumerationState __enumState = {0};
id __objects[MAX_STACKBUFF_SIZE];
NSUInteger __count;
while ((__count = [myArray countByEnumeratingWithState:&__enumState objects:__objects count:MAX_STACKBUFF_SIZE]) > 0) {
for (NSUInteger i = 0; i < __count; i++) {
id obj = __objects[i];
[obj doSomething];
}
}
The problem is that, I found it wrong.
First of all, when you have Automatic Reference Counting (ARC) turned on, you got an error
Sending '__strong id *' to parameter of type '__unsafe_unretained_id*' changes retain/release properties of pointer
But even when I turn ARC off I found out that I __object array seems to behave strangely :
This is actual Code (I assumed MAX_STACKBUFF_SIZE to be 40):
#autoreleasepool {
NSArray *myArray = #[#"a", #"b", #"c", #"d", #"e", #"f", #"g"];
int MAX_STACKBUFF_SIZE = 40;
NSFastEnumerationState __enumState = {0};
id __objects[MAX_STACKBUFF_SIZE];
NSUInteger __count;
while ((__count = [myArray countByEnumeratingWithState:&__enumState objects:__objects count:MAX_STACKBUFF_SIZE]) > 0) {
for (NSUInteger i = 0; i < __count; i++) {
id obj = __objects[i];
__enumState.itemsPtr
NSLog(#" Object from __objects ! %#", obj); // on screenshot different message
}
}
}
return 0;
I got EXC_BAD_ACESS when I try to get the contents of the __object array.
I also found out that when you try to iterate through __enumState.itemsPtr it actually works.
Could you explain me what is going on here ? Why my __objects seems to be "shrunken down". And why it doesn't contains desired object? And why is that error when ARC is turned on.
Thank you very very much in advance for your time and effort! (I provided screenshot for better understanding what causes an error)
First of all, strong pointers cannot be used in C-structures, as explained in the "Transitioning to ARC Release Notes", therefore the objects array has be be declared
as
__unsafe_unretained id __objects[MAX_STACKBUFF_SIZE];
if you compile with ARC.
Now it is not obvious (to me) from the NSFastEnumeration documentation, but it is
explained in Cocoa With Love:Implementing countByEnumeratingWithState:objects:count:
that the implementation need not fill the supplied objects array, but can just set
__enumState.itemsPtr to an existing array (e.g. some internal storage). In that case, the contents of the
__objects array is undefined, which causes the crash.
Replacing
id obj = __objects[i];
by
id obj = __enumState.itemsPtr[i];
gives the expected result, which is what you observed.
Another reference can be found in the "FastEnumerationSample" sample code:
You have two choices when implementing this method:
1) Use the stack
based array provided by stackbuf. If you do this, then you must
respect the value of 'len'.
2) Return your own array of objects. If
you do this, return the full length of the array returned until you
run out of objects, then return 0. For example, a linked-array
implementation may return each array in order until you iterate
through all arrays.
In either case, state->itemsPtr MUST be a valid
array (non-nil). ...

Resources