Does NSDictionary initWithObjectsAndKeys handles NULL/nil objects and other than NSString objects - objective-c

I am trying to initialize NSDictionary but it gives me following error:
Program received signal: “EXC_BAD_ACCESS”.
Here is my problematic code[Quiz.m]:
#implementation Quiz
#synthesize question;
#synthesize correctAnswer;
#synthesize userAnswer;
#synthesize questionId;
-(NSString*) getAsJsonString
{
// Following line gives the error
NSDictionary *qDictionary=[[NSDictionary alloc] initWithObjectsAndKeys:question,#"questionText",questionId,#"questionId",userAnswer,#"userAnswer",correctAnswer,#"correctAnswer",nil];
..............
.........
............
return jsonString;
}
#end
And here is Quiz.h file for reference
#interface Quiz : NSObject {
#public
NSString * question;
BOOL correctAnswer;
BOOL userAnswer;
NSInteger questionId;
}
#property (nonatomic,retain) NSString * question;
#property (nonatomic, assign) NSInteger questionId;
#property (nonatomic,assign) BOOL correctAnswer;
#property (nonatomic,assign) BOOL userAnswer;
- (NSString*) getAsJsonString;
#end
How should I fix it, please help me, I am new to objective c and it is driving me nuts. Does the NSDictionary initWithObjectsAndKeys handle objects other than string and null objects?

NSDictionary cannot store scalar values (like BOOL, NSInteger, etc.), it can store only objects. You must wrap your scalar values into NSNumber to store them:
NSDictionary *qDictionary = [[NSDictionary alloc]
initWithObjectsAndKeys:question,#"questionText",
[NSNumber numberWithInteger:questionId],#"questionId",
[NSNumber numberWithBool:userAnswer],#"userAnswer",
[NSNumber numberWithBool:correctAnswer],#"correctAnswer",
nil];

Related

Creating Dictionary from a model that contains nil values

I have the following Model:
#interface Person : NSObject
#property (nonatomic, copy) NSString *firstName;
#property (nonatomic, copy) NSString *middleName;
#property (nonatomic, copy) NSString *lastName;
#property (nonatomic, copy) NSString *status;
#property (nonatomic, copy) NSString *favoriteMeal;
#property (nonatomic, copy) NSString *favoriteDrink;
#property (nonatomic, copy) NSString *favoriteShow;
#property (nonatomic, copy) NSString *favoriteMovie;
#property (nonatomic, copy) NSString *favoriteSport;
-(NSDictionary *)getSomeInfo;
-(NSDictionary *)getAllInfo;
#end
Part 1:
I want getSomeInfo to return NSDictionary (e.g. {"firstName", self.firstName}) for all the fields that does not contain nil. How can I do that? (I could check every value but I wonder if there's a better way)
Part 2:
I want getAllInfo to return NSDictionary with all the property and if one contains nil then it should throw an error. Again do I have to write a long conditional statement to check or is there a better way?
Note: I want to do this without using external library. I'm new to the language so I'm open to suggestions if there's a better pattern in Objective-C.
There are two approaches.
1) Check each value:
- (NSDictionary *)getSomeInfo {
NSMutableDictionary *res = [NSMutableDictionary dictionary];
if (self.firstName.length) {
res[#"firstName"] = self.firstName;
}
if (self.middleName.length) {
res[#"middleName"] = self.middleName;
}
// Repeat for all of the properties
return res;
}
2) Use KVC (Key-value coding):
- (NSDictionary *)getSomeInfo {
NSMutableDictionary *res = [NSMutableDictionary dictionary];
NSArray *properties = #[ #"firstName", #"middleName", #"lastName", ... ]; // list all of the properties
for (NSString *property in properties) {
NSString *value = [self valueForKey:property];
if (value.length) {
res[property] = value;
}
}
return res;
}
For the getAllInfo method you can do the same but instead return nil if any value is missing. Treat the nil results as your indication that not all properties have a value.

Can't use NSMutableArray data after Parsing with NSXMLParser (objective-c)

Hi I did parsing with NSXMLParser of some xml :
<company>
<name>Idan</name>
<country>Israel</country>
.....
<gender>man</gender>
</company>
I see that parsing success , now I have the MutableArray with one object that contain all strings (Idan,Israel etc.) but when I want to use this array, I can't get strings it contain.
When I do :
NSMutableArray *use = [pars users ];
NSLog(#"%#",use );
(users it's my array with object) I see:
<List:03f5a78>
where List in my code is:
#import <Foundation/Foundation.h>
#interface List : NSObject{
NSString *name;
NSString *country;
NSString *status;
NSString *gender;
}
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *country;
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) NSString *gender;
#end
#import "List.h"
#implementation List
#synthesize name,date,city,country,status, gender;
#end
I try to do something like this:
NSMutableArray *use = [pars users.name ];
NSLog(#"%#",use );
but is not working, any ideas how to fix this?
Override the description method of your List class, and return a string which includes the values of all of the properties, then output it like you did the first time. The console will then print the value you returned.
Example:
#implementation List
...
...
-(NSString *)description
{
NSMutableString *desc = [NSMutableString string];
[desc appendFormat:#"name=%#, ", self.name];
[desc appendFormat:#"country=%#, ", self.country];
[desc appendFormat:#"status=%#, ", self.status];
[desc appendFormat:#"gender=%#", self.gender];
return desc
}
...
...
#end

Store scalar values (NSInteger, BOOL, double) in CoreData without converting to NSNumber

I have read a lot of articles where they say to explicitely convert from and to NSNumber when I want to store scalars in CoreData:
#property (nonatomic, assign) NSInteger value;
- (NSInteger)value
{
return [value integerValue];
}
- (void)setValue:(NSInteger)val
{
value = [NSNumber numberWithInteger:val];
}
But in our old project we have a bunch of properties where we doesn't do those manipulations (and they don't have custom accessors)! Why it works?
Example code
Declaration. Scalar values are not transient.
#interface ProductProperty : NSManagedObject
#property (nonatomic, strong, readonly) NSString * propertyID;
#property (nonatomic, strong, readonly) NSString * title;
#property (nonatomic, strong, readonly) NSSet * values;
#property (nonatomic, assign, readonly) BOOL filter;
#property (nonatomic, strong, readonly) NSDate *update;
#property (nonatomic, strong, readonly) NSNumber *index;
#property (nonatomic, assign, readonly) BOOL system;
#end
#import "ProductProperty.h"
#implementation ProductProperty
#dynamic propertyID;
#dynamic title;
#dynamic values;
#dynamic filter;
#dynamic update;
#dynamic index;
#dynamic system;
#end
Mapping into objects. Called if received JSON differs from existing. Otherwise it fetches from the CoreData storage.
- (void)updateProperties:(NSArray*)properties
{
for (NSDictionary *_property in properties) {
NSString *propertyID = [_property objectForKey:#"id"];
ProductProperty *property = [state.productPropertiesWithIDs objectForKey:propertyID];
if (!property) {
property = [state.ctx newObjectWithEntityName:ProductProperty.entityName];
property.propertyID = propertyID;
[state.productPropertiesWithIDs setObject:property forKey:propertyID];
}
property.update = state.update;
property.title = [_property objectForKey:#"title"];
property.filter = [_property objectForKey:#"filter"] ? [[_property objectForKey:#"filter"] boolValue] : YES;
property.index = [propertyIndexes objectForKey:propertyID] ? [propertyIndexes objectForKey:propertyID] : [NSNumber numberWithInt:propertyIndex++];
property.system = [SYSTEM_PROPERTY_IDS containsObject:propertyID] ? YES : NO;
[self updatePropertyValues:[_property objectForKey:#"values"] forProperty:property];
}
}
- (ProductProperty*)productPropertyWithID:(NSString*)propertyId error:(NSError**)error
{
NSFetchRequest *req = [ProductProperty request];
req.predicate = [NSPredicate predicateWithFormat:#"propertyID == %#", propertyId];
return [[ctx executeFetchRequest:req error:error] lastObject];
}
The answer is that since iOS 5 CoreData support auto generating accessors for scalars so I don't need to implement them manually.

this method crashes if I use a property and works fine if I declare the variable within the method--why?

I'm working on a programmable calculator, and for the life of me I can't understand what I'm doing wrong.
Here are the relevant parts of the code. (The code is unfinished, so I know there's extra stuff floating around.)
CalculatorViewController.m
#import "CalculatorViewController.h"
#import "CalculatorBrain.h"
#interface CalculatorViewController ()
#property (nonatomic) BOOL userIsEnteringNumber;
#property (nonatomic) BOOL numberIsNegative;
#property (nonatomic,strong) CalculatorBrain *brain;
#property (nonatomic) NSArray *arrayOfDictionaries;
#property (nonatomic) NSDictionary *dictionary;
#end
#implementation CalculatorViewController
#synthesize display = _display;
#synthesize history = _history;
#synthesize userIsEnteringNumber = _userIsEnteringNumber;
#synthesize numberIsNegative;
#synthesize brain = _brain;
#synthesize arrayOfDictionaries;
#synthesize dictionary;
-(CalculatorBrain *)brain
{
if (!_brain) _brain = [[CalculatorBrain alloc] init];
return _brain;
}
/*snip code for some other methods*/
- (IBAction)variablePressed:(UIButton *)sender
{
NSString *var = sender.currentTitle;
NSDictionary *dict = [self.dictionary initWithObjectsAndKeys:[NSNumber numberWithDouble:3],#"x",[NSNumber numberWithDouble:4.1],#"y",[NSNumber numberWithDouble:-6],#"z",[NSNumber numberWithDouble:8.7263],#"foo",nil];
[self.brain convertVariable:var usingDictionary:dict];
self.display.text = [NSString stringWithFormat:#"%#",var];
self.history.text = [self.history.text stringByAppendingString:sender.currentTitle];
[self.brain pushOperand:[dict objectForKey:var] withDictionary:dict];
}
#end
And here's CalculatorBrain.m.
#import "CalculatorBrain.h"
#interface CalculatorBrain ()
#property (nonatomic, strong) NSMutableArray *operandStack;
#end
#implementation CalculatorBrain
#synthesize operandStack = _operandStack;
-(void)pushOperand:(id)operand withDictionary:(NSDictionary *)dictionary
{
NSNumber *operandAsObject;
if (![operand isKindOfClass:[NSString class]])
{
operandAsObject = operand;
}
else
{
operandAsObject = [dictionary objectForKey:operand];
}
[self.operandStack addObject:operandAsObject];
}
-(double)popOperand
{
NSNumber *operandAsObject = [self.operandStack lastObject];
if (operandAsObject) [self.operandStack removeLastObject];
return [operandAsObject doubleValue];
}
-(double)convertVariable:(NSString *)variable usingDictionary:dictionary
{
double convertedNumber = [[dictionary objectForKey:variable] doubleValue];
return convertedNumber;
}
#end
The thing I'm having trouble understanding is in the CalculatorViewController.m method - (IBAction)variablePressed:(UIButton *)sender. This line crashes the program:
NSDictionary *dict = [self.dictionary initWithObjectsAndKeys:[list of objects and keys]];
But if I make it
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:[list of objects and keys]];
then everything works fine. But if I try to do
NSDictionary *dict = [[self.dictionary alloc] initWithObjectsAndKeys:[list of objects and keys]];
which seems to me the right thing to do, then XCode won't let me, so I'm obviously not understanding something.
Any thoughts?
+alloc allocates memory for an object. -init... methods initialize the object.
[self.dictionary initWithObjectsAndKeys:... calls -dictionary which is either going to return a dictionary set in that property or nil and then attempts to call init... on it. If the dictionary exists then you are attempting to initialize an object more than once which is not valid. If the property has not been set then the getter will return nil and sending an init... message to nil will do nothing. Either way this is not what you want to do.
[[self.dictionary alloc] init... is also invalid, as the compiler warns you. Now you try to obtain an object from -dictionary and then call the class method +alloc on it.
There seems to be some fundamental confusion here about how objects are created and what property accessors do. I'm not sure how to address that besides suggesting looking at object creation and dot syntax.

NSMutableArray: add and extract struct

I'm trying to store some data in an NSMutableArray. This is my struct:
typedef struct{
int time;
char name[15];
}person;
This is the code to add a person:
person h1;
h1.time = 108000;
strcpy(h1.name, "Anonymous");
[highscore insertObject:[NSValue value:&h1 withObjCType:#encode(person)] atIndex:0];
So, I try to extract in this way:
NSValue * value = [highscore objectAtIndex:0];
person p;
[value getValue:&p];
NSLog(#"%d", p.time);
The problem is that the final log doesn't show me 108000!
What is wrong?
Your code looks correct (and works for me), so I deduce that you aren't initializing highscore. So when you send the insertObject:atIndex: message to it, nothing happens. When you then send the objectAtIndex: method to it, you get nil back. When you send getValue: to the nil NSValue *value, it does nothing, so your person p is left filled with random stack garbage, which is why your NSLog doesn't print 108000.
As stated in my initial comment there rarely is a reason to do this kind of stuff with pure c structs. Instead go with real class objects:
If you're unfamiliar with the syntax below you may want to look at these quick tutorials on ObjC 2.0 as well as read Apple's documentation:
A Quick Objective-C 2.0 Tutorial
A Quick Objective-C 2.0 Tutorial: Part II
Person Class:
// "Person.h":
#interface Person : NSObject {}
#property (readwrite, strong, nonatomic) NSString *name;
#property (readwrite, assign, nonatomic) NSUInteger time;
#end
// "Person.m":
#implementation Person
#synthesize name = _name; // creates -(NSString *)name and -(void)setName:(NSString *)name
#synthesize time = _time; // creates -(NSUInteger)time and -(void)setTime:(NSUInteger)time
#end
Class use:
#import "Person.h"
//Store in highscore:
Person *person = [[Person alloc] init];
person.time = 108000; // equivalent to: [person setTime:108000];
person.name = #"Anonymous"; // equivalent to: [person setName:#"Anonymous"];
[highscore insertObject:person atIndex:0];
//Retreive from highscore:
Person *person = [highscore objectAtIndex:0]; // or in modern ObjC: highscore[0];
NSLog(#"%#: %lu", person.name, person.time);
// Result: "Anonymous: 108000"
To simplify debugging you may also want Person to implement the description method:
- (NSString *)description {
return [NSString stringWithFormat:#"<%# %p name:\"%#\" time:%lu>", [self class], self, self.name, self.time];
}
which will allow you to just do this for logging:
NSLog(#"%#", person);
// Result: "<Person 0x123456789 name:"Anonymous" time:108000>
Reimplement Person as an Objective-C object and reap the benefits:
Person.h:
#interface Person : NSObject
{
int _time;
NSString *_name;
}
#property (assign, nonatomic) int time;
#property (retain, nonatomic) NSString *name;
#end
Person.m:
#import "Person.h"
#interface Person
#synthesize time = _time;
#synthesize name = _name;
- (id)init
{
self = [super init];
if (self != nil)
{
// Add init here
}
return self;
}
- (void)dealloc
{
self.name = nil;
[super dealloc];
}
#end