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.
Related
I am a newcomer to objective C and I have serious problems in accessing NSMutableDictionarys.
I have two objects (Network and Beacon), and I want to create a NSMutableDictionary of Networks with a NSMutableDictionary of Beacons inside.
Network.h
#import <Foundation/Foundation.h>
#interface Network : NSObject{
NSString *id_network;
NSString *major;
NSString *active;
NSString *name;
NSString *status;
NSMutableDictionary *beaconsDictionary;
}
#property (nonatomic, strong) NSString *id_network;
#property (nonatomic, strong) NSString *major;
#property (nonatomic, strong) NSString *active;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) NSMutableDictionary *beaconsDictionary;
#end
Beacon.h
#import <Foundation/Foundation.h>
#interface Beacon : NSObject{
NSString *id_beacon;
NSString *major;
NSString *minor;
NSString *active;
NSString *detected;
}
#property (nonatomic, strong) NSString *id_beacon;
#property (nonatomic, strong) NSString *major;
#property (nonatomic, strong) NSString *minor;
#property (nonatomic, strong) NSString *active;
#property (nonatomic, strong) NSString *detected;
#end
I can create the NSMutableDictionary like this:
Beacon *beacon = [[Beacon alloc]init];
beacon.id_beacon=#"1";
beacon.major=#"1";
beacon.minor=#"1";
beacon.active=#"1";
beacon.detected=#"0";
NSMutableDictionary *beaconDic = [[NSMutableDictionary alloc]init];
[beaconDic setObject:beacon forKey:beacon.id_beacon];
Network *net = [[Network alloc]init];
net.id_network=#"1";
net.major=#"1";
net.active=#"1";
net.name=#"network 1";
net.status=#"1";
net.beaconsDictionary=beaconDic;
NSMutableDictionary *networkDic = [[NSMutableDictionary alloc]init];
[networkDic setObject:net forKey:net.id_network];
Ok, but now how can i access to beacon property "detected" directly and modify it?
I know this this is a very bad example, but I have no idea how to do it.
It looks like you'll have to have a network id and a beacon id to get where you need to go. It would looks something like:
Network *net = networkDic[netId];
Beacon *beacon = net.beaconsDictionary[beaconId];
beacon.detected = newDetectedValue;
This is for arbitrary network ids and beacon ids. You can hardcode values if you wish.
Edit:
It's worth noting in your example code that you can use the more modern dictionary assignment. Rather than [dictionary setValue:value forKey:key];, you can do dictionary[key] = value;. It's, of course, personal preference but you're very likely to see the latter in more recent things and I find it to be clearer.
You can get your Network and Beacon objects back by providing keys that match their keys in the dictionary:
NSString *nwKey = #"1";
Network *n = networkDic[nwKey];
NSDictionary *bDict = n.beaconsDictionary;
NSString *bnKey = #"1";
Beacon *b = bDict[bnKey];
Note: This is the new syntax. Here is the old one:
NSString *nwKey = #"1";
Network *n = [networkDic objectForKey:nwKey];
NSDictionary *bDict = n.beaconsDictionary;
NSString *bnKey = #"1";
Beacon *b = [bDict objectForKey:bnKey];
Really strange question but I just can't find the right way to do this on the internet my self.
I have 3 NSStrings. called: string1, string2 and string3.
They all get value from the same UITextfield but at different times. So the values are different from each other.
What happens now is:
-(void)statement {
if (i==0) {
string1 = nameField.text;
} else if (i==1) {
string2 = nameField.text;
} else if (i==2) {
string3 = nameField.text;
}
}
Is it possible to replace the 1,2 and 3 of behind the 'string' with a variable or something so I can say something like:
-(void)statement {
stringX = nameField.text;
}
So that I can change X before the statement is activated?
Hope that it's all clear!
Thanks!
Declare a mutable array called, for example, myString
NSMutableArray *myString = [[NSMutableArray alloc] init];
then use something like
myString[i] = nameField.text
If those strings are properties on an object, you can use KVO:
#property (nonatomic, strong) NSString *string1;
#property (nonatomic, strong) NSString *string2;
#property (nonatomic, strong) NSString *string3;
Setter:
[object setValue:nameField.text forKey:[NSString withFormat:#"string%#", #(i)]];
Or create a selector and perform it:
// assuming
#property (nonatomic, strong) NSString *string1;
#property (nonatomic, strong) NSString *string2;
#property (nonatomic, strong) NSString *string3;
// then
-(void)statement {
NSString *selName = [NSString withFormat:#"setString%#", #(i)];
SEL sel = NSSelectorFromString(selName);
[self performSelector:sel withObject:nameField.text afterDelay:0];
}
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
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.
- (id)copyWithZone:(NSZone *)zone {
PoolFacility *copy = [[[self class] allocWithZone:zone]init];
copy.name = [self.name copy];
copy.type = [self.type copy];
copy.phoneNumber = [self.phoneNumber copy];
//make sure I get proper copies of my dictionaries
copy.address = [self.address mutableCopy];
copy.webAddress = [self.webAddress copy];
copy.prices = [self.prices mutableCopy];
copy.pools = [self.pools mutableCopy];
return copy;
}
Can anyone see any memory leaks?
Here's the property types:
NSString *name;
NSString *type;
NSMutableDictionary *address;
NSString *phoneNumber;
NSString *webAddress;
NSMutableArray *prices;
NSMutableArray *pools;
Here are the property declarations:
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *type;
#property (nonatomic, copy) NSString *phoneNumber;
#property (nonatomic, retain) NSMutableDictionary *address;
#property (nonatomic, copy) NSString *webAddress;
#property (nonatomic, retain) NSMutableArray *prices;
#property (nonatomic, retain) NSMutableArray *pools;
The properties defined as copy and not retain will have an extra copy when set as below (your code)
copy.name = [self.name copy];
copy.type = [self.type copy];
copy.phoneNumber = [self.phoneNumber copy];
copy.webAddress = [self.webAddress copy];
it should be sufficient to only write them as
copy.name = self.name;
copy.type = self.type;
copy.phoneNumber = self.phoneNumber;
copy.webAddress = self.webAddress;
This almost certainly leaks like a sieve. You need to provide your #property and other method declarations for us to recommend the best way to fix it.