Is there any generic implementation which converts any Object into NSDictionary, sets all variable names as keys and values as dictionary values?
In order to achieve your objective, you can use Key-Value Coding. This protocol provides a mechanism to set values of object properties based on the names of the properties represented as NSString's rather than calling the accessors directly.
In order for it to work, you need to have defined your objects with accessors that follow the naming conventions (easy enough using properties). You can see the NSKeyValueCoding protocol guide here:
http://bit.ly/es6kyH
And the Key-Value Coding programming guide here:
http://bit.ly/fBY3Qa
You'll still have to do the iteration, but it's a good start.
Solved using SBJSONParser, converted NSObject to JSON Representation and then fetched NSDictionary out of it.
The perfect way to do this is by using a library for serialization/deserialization
many libraries are available but one i like is
JagPropertyConverter
https://github.com/jagill/JAGPropertyConverter
it can convert your Custom object into NSDictionary and vice versa
Assuming
#interface Person : NSObject
#property (nonatomic, retain) NSNumber * age
#property (nonatomic, retain) NSString * address
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) NSString * cellNo;
#end
JAGPropertyConverter *converter = [[JAGPropertyConverter alloc]init];
converter.classesToConvert = [NSSet setWithObjects:[Person class], nil];
Person *person = [[Person alloc]init];
person.name = #"name";
person.address = #"address";
person.age = #27;
person.cellNo = #"XXXXXXXXXX";
//For Object to Dictionary
NSDictionary *dictPerson = [converter convertToDictionary:person];
//For Dictionary to Object
Person *person = [[Person alloc]init];
[converter setPropertiesOf:person fromDictionary:dictPerson];
No, you could have everything in your class, e.g. references to other objects and primitives. NS-Dictionary can serialize itself to a NSString and NSString can recreate the dictionary from the string. Best will be you supply your own methods to serialize it.
Related
I have 6 categories that contain unique data; I have defined a class that looks like this:
#interface ExportBookData : NSObject {
}
#property (strong, nonatomic) NSArray *booksArray;
#property (nonatomic, retain) NSMutableDictionary *builtFileList;
#property (nonatomic, retain) NSMutableArray *exportData;
#end
What I want to do is be able to instantiate the class ExportBookData, once for each category, and use that instantiated class throughout another class, having the data persist and be accessible between classes.
I have tried this code, but it doesn't do what I need:
ExportBookData *abe = [ExportBookData new];
abe.abeBuiltFileList = [NSMutableDictionary dictionary];
abe.abeExportData = [NSMutableArray arrayWithCapacity:abe.abeBooksArray.count];
UPDATE The problem is in the addressing of the objects; I have categories named Abe, Balls, Comp, Caller, Hut, and House. I want the class to have properties that can be addressed as Abe, Balls, etc. I can't figure out how to do that with what I have defined.
I have looked through Google, but found nothing that answers my specific question.
Encapsulate, encapsulate, encapsulate! Put the special knowledge inside the class itself.
Let's say you have an ExportBookData object that behaves differently depending which bookseller it uses. Then provide an initializer that takes a bookseller type:
ExportBookData *abe = [[ExportBookData alloc] initWithCategory:#"Abe"];
Okay, so now this instance of ExportBookData knows that its behavior should be Abe-type behavior. But no matter how an ExportBookData is initialized, its public property names will all be the same, e.g. builtFileList and exportData, so you'll then be able to refer to abe.builtFileList and this will be the right kind of list for an Abe.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why is my object not key value coding-compliant?
I'm having a dictionary and I want to add keys/values to a custom class, but i always get the error, that the class is not KVC compliant, but the Apple documents state that it should be.
My code:
ContactObject.h:
#interface ContactObject : NSObject
+ (ContactObject *)testAdding;
#end
ContactObject.m:
#implementation ContactObject
- (id)init {
self = [super init];
if (self) {
// customize
}
return self;
}
+ (ContactObject *)testAdding
{
// create object
ContactObject *theReturnObject = [[ContactObject alloc] init];
[theReturnObject setValue:#"Berlin" forKey:#"city"];
[theReturnObject setValue:#"Germany" forKey:#"state"];
return theReturnObject;
}
#end
I think I'm missing something very stupid :)
Please, any help appreciated ...
Greetings,
matthias
Actually to be KVC compliant:
How you make a property KVC compliant depends on whether that property is an attribute, a to-one relationship, or a to-many relationship. For attributes and to-one relationships, a class must implement at least one of the following in the given order of preference (key refers to the property key):
The class has a declared property with the name key.
It implements accessor methods named key and, if the property is mutable, setKey:. (If the property is a Boolean attribute, the getter accessor method has the form isKey.)
It declares an instance variable of the form key or _key.
I don't see any of these three implemented. You need to have at least properties that you are trying to set through KVC, the default NSObject implementation is able to set properties through setValue:forKey: but you must declare them.
You need to declare every property that will be used:
#interface ContactObject : NSObject
#property (nonatomic,copy, readwrite) NSString* city;
#property (nonatomic, copy, readwrite) NSString* state;
+ (ContactObject *)testAdding;
#end
Or use a NSMutableDictionary object.
For example:
NSMutableDictionary* dict= [NSMutableDictionary new];
[dict setObject: #"Berlin" forKey: #"city"];
[dict setObject: #"Germany" forKey: #"state"];
You need to actually declare/implement properties. Key-Value Coding doesn't mean that every NSObject is automatically a key/value dictionary.
In this case you would need to declare:
#property (nonatomic, readwrite, copy) NSString* city;
#property (nonatomic, readwrite, copy) NSString* state;
in your #interface declaration.
ObjC is dynamic in some ways, but it's not really dynamic as far as storage in classes. If you want ContactObject to be KVC-compliant for certain keys, those keys need to exist in the class. The KVC Guide has this to say:
For properties that are an attribute or a to-one relationship, this
requires that your class:
Implement a method named -<key>, -is<Key>, or have an instance
variable <key> or _<key>. Although key names frequently begin with a
lowercase letter, KVC also supports key names that begin with an
uppercase letter, such as URL.
If the property is mutable, then it should also implement -set<Key>:.
Your implementation of the -set<Key>: method should not perform
validation.
The easiest way to accomplish that is to declare the keys you want as properties:
#property (copy, nonatomic) NSString * city;
#property (copy, nonatomic) NSString * state;
You can also declare an ivar and implement accessors yourself, but there's usually no good reason to do it that way -- declared properties will take good care of you.
I have a class, say the class Person. In than class I have several NSMutableArrays.
#property NSMutableArray *arrayOne;
#property NSMutableArray *arrayTwo;
...
Now I want to copy that Class so I can return it from a function and use the copy and change it's data. I want to have a new copy of the object in memory not another reference to the same address.
To do that I have implemented this in my Person.m:
-(id)copyWithZone:(NSZone *)zone
{
Person *copy = [[Person allocWithZone:zone] init;
copy.arrayOne = [NSMutableArray arrayWithArray:self.arrayOne];
copy.arrayTwo = [NSMutableArray arrayWithArray:self.arrayTwo];
...
return copy;
}
So far this works just like I want it to, but when I try to sort the arrays of the copy, I get an error:
-[__NSArrayI sortUsingSelector:]: unrecognized selector sent to instance 0x100108a30
I have noticed that the original arrays are of the type '_NSArrayM' but the copy is '_NSArrayI'...
So what did I wrong? I have heard of deep copying using NSArchiver and NSUnarchiver... Do I have to use that? Sorry, I am quite new to C and Objective-C... :D
I hope you can help me out of this. =)
Sorry about my 'school-english'....
Cheers,
Nick
EDIT: The arrays consist of NSString objects, so I can use
sortUsingSelector:#selector(caseInsensitiveCompare:)
to sort the array.
How are your mutable array properties being declared in your header file?
// Wrong
#property (copy) NSMutableArray *array;
#property (assign) NSMutableArray *array;
// Right
#property (strong) NSMutableArray *array;
NB If you're just doing
#property NSMutableArray *array;
then I'm amazed your code has got this far :) The default property memory semantics is assign - which won't tell ARC to retain your arrays in any way at all :) You need to specify strong.
I've seen a lot of people use NSDictionary for JSON parsing:
//ViewController.m
NSString* forename = [jsonDict valueForKey:#"forename"];
NSString* surname = [jsonDict valueForKey:#"surname"];
But I've also people creating custom NSObjects from a NSDictionary.
//JSONObject.h
#interface JSONObject : NSObject
#property (nonatomic) NSString* forename;
#property (nonatomic) NSString* surname;
#end
//JSONObject.m
#implementation JSONObect
#synthesize forename = _forename;
#synthesize surname = _surname;
#end
//ViewController.m
JSONObject* jsonObject = [[JSONObject alloc] init];
[jsonObject setForename:[jsonDict valueForKey:#"forename"]];
[jsonObject setSurname:[jsonDict valueForKey:#"surname"]];
And then store these in a NSMutableArray:
NSMutableArray* jsonObjectsArray = [NSMutableArray arrayWithCapacity:20];
[jsonObjectsArray addObject:jsonObject];
Which can be accessed later if needed.
In my case, I have a UITableView that gets it's data from JSON. The data is used at least once but most likely will be used more (eg. on device rotation). The JSON data shouldn't be permanently stored to file as it is updated regularly and is downloaded every time the app launches.
Should I use a custom NSObject or a NSDictionary in my scenario?
One argument for using a custom Object is that it is a few short steps away from using a NSManagedObject, which would let you leverage Core Data to manage your object graph.
The argument for using the NSDictionary is that it's simpler and easier to understand, and you define fewer "minor" classes (and associated h/m files), so less to manage in a project. Also a lot easier to edit/extend in a project "in flux".
I'm fairly new to Objective-C and am not sure how to correctly deal with memory management in the following scenario:
I have a Core Data Entity with a to-many relationship for the key "children". In order to access the children as an array, sorted by the column "position", I wrote the model class this way:
#interface AbstractItem : NSManagedObject
{
NSArray * arrangedChildren;
}
#property (nonatomic, retain) NSSet * children;
#property (nonatomic, retain) NSNumber * position;
#property (nonatomic, retain) NSArray * arrangedChildren;
#end
#implementation AbstractItem
#dynamic children;
#dynamic position;
#synthesize arrangedChildren;
- (NSArray*)arrangedChildren
{
NSArray* unarrangedChildren = [[self.children allObjects] retain];
NSSortDescriptor* sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"position" ascending:YES];
[arrangedChildren release];
arrangedChildren = [unarrangedChildren sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[sortDescriptor release];
[unarrangedChildren release];
return [arrangedChildren retain];
}
#end
I'm not sure whether or not to retain unarrangedChildren and the returned arrangedChildren (first and last line of the arrangedChildren getter). Does the NSSet allObjects method already return a retained array? It's probably too late and I have a coffee overdose.
I'd be really thankful if someone could point me in the right direction. I guess I'm missing vital parts of memory management knowledge and I will definitely look into it thoroughly.
-allObjects returns an autoreleased instance, there is no need to retain and release it.
As for arrangedChildren, it will be retained only if you use the synthesized setter:
self.arrangedChildren = [unarrangedChildren sortedArrayUsingDescriptors:/*...*/];
Directly assigning to the instance variable as you do does not invoke the synthesized setter.
Finally, you shouldn't retain the return value here - your method isn't named starting with alloc, new or create and callers thus have to take ownership explicitly.
I recommend reading the "Cocoa Memory Management Guide" and the section on "Declared Properties" in the Objective-C language description.
The retain in your #property for arrangedChildren should take care of that. You will need to release it in your dealloc.