#interface Esame : NSObject{
NSString *nome;
int voto;
int crediti;
int anno;
}
#property (nonatomic, retain) NSString *nome;
- (id)initWithNome:(NSString*)nome voto:(int)voto crediti:(int)crediti anno:(int)anno;
#end
This is my implementation
#import "Esame.h"
#implementation Esame
#synthesize nome;
- (id)initWithNome:(NSString*)name voto:(int)voto crediti:(int)crediti anno:(int)anno {
if ((self = [super init])) {
self.nome = name;
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
nome = [decoder decodeObjectForKey:#"nome"] ;
voto = [decoder decodeIntForKey:#"voto"];
crediti = [decoder decodeIntForKey:#"crediti"];
anno = [decoder decodeIntForKey:#"anno"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder {
if (nome) [encoder encodeObject:nome forKey:#"nome"];
if (voto) [encoder encodeInt:voto forKey:#"voto"];
if (crediti) [encoder encodeInt:crediti forKey:#"crediti"];
if (anno) [encoder encodeInt:anno forKey:#"anno"];
}
#end
I receive same strange error... expecially in the NSString... what's wrong?
Try removing your conditionals before your encodeInt:; you should probably always encode all members. Also, you should probably declare that you conform to NSCoding with #interface Esame : NSObject<NSCoding>.
If this doesn't work, try posting the error message you're seeing.
Related
I'd like to archive my custom objects. ClassA holds a dictionary whose values are instances of ClassB.
Everything looks good ClassA's initWithCoder, and _dictionary is declared strong, but after init returns and I call callMeAfterInit, the _dictionary is empty (not null, a valid empty dictionary).
This ought to be simple, but I haven't used archiving much, so maybe I'm missing something basic. What might cause the dictionary to be emptied after the init returns?
Here's the essential code:
#interface ClassA : NSObject <NSCoding>
#property (strong, nonatomic) NSMutableDictionary *dictionary;
#end
#implementation ClassA
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.dictionary forKey:#"dictionary"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self) {
_dictionary = [decoder decodeObjectForKey:#"dictionary"];
// breakpoint here and I can inspect a good dictionary, full of ClassB values
}
return self;
}
- (void)callMeAfterInit {
NSLog(#"%#", self.dictionary);
// Log output here shows an empty dictionary (valid, but with 0 pairs)
}
#end
#interface ClassB : NSObject <NSCoding>
#property (strong, nonatomic) NSNumber *number;
#property (strong, nonatomic) NSArray *array;
#end
#implementation ClassB
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.number forKey:#"number"];
[encoder encodeObject:self.array forKey:#"array"];
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (self) {
_number = [decoder decodeObjectForKey:#"number"];
_array = [decoder decodeObjectForKey:#"array"];
}
return self;
}
#end
You don't show the code that calls callMeAfterInit, so this is a guess.
More likely than not, you aren't talking to the same instance of ClassA. Did you happen to call [[ClassA alloc] init]; somewhere?
This question already has answers here:
Objective C - How do I use initWithCoder method?
(2 answers)
Closed 9 years ago.
I was reading about initializing the archived objects from a XIB file and found that
- (id)initWithCoder:(NSCoder *)aDecoder
is a way of doing it. But I am not able to get a hang around this. Can someone show me an simple example of how to do this?
Thanks a ton
The NSCoder class is used to archive/unarchive (marshal/unmarshal, serialize/deserialize) of objects.
This is a method to write objects on streams (like files, sockets) and being able to retrieve them later or in a different place.
I would suggest you to read Archiving
You also need to define the following method as follows:
- (void)encodeWithCoder:(NSCoder *)enCoder
{
[super encodeWithCoder:enCoder];
[enCoder encodeObject:instanceVariable forKey:INSTANCEVARIABLE_KEY];
// Similarly for the other instance variables.
....
}
And in the initWithCoder method initialize as follows:
- (id)initWithCoder:(NSCoder *)aDecoder
{
if(self = [super initWithCoder:aDecoder]) {
self.instanceVariable = [aDecoder decodeObjectForKey:INSTANCEVARIABLE_KEY];
// similarly for other instance variables
....
}
return self;
}
You can initialize the object standard way i.e
CustomObject *customObject = [[CustomObject alloc] init];
Example taken from this answer
You can use it in following way:
.h file
#interface Score : NSObject {
NSString *Username;
NSString *TotalPoints;
NSString *LifeRemains;
NSString *ScoreDate;
}
#property (nonatomic, retain) NSString *Username;
#property (nonatomic, retain) NSString *TotalPoints;
#property (nonatomic, retain) NSString *LifeRemains;
#property (nonatomic, retain) NSString *ScoreDate;
in .m file
#synthesize Username, TotalPoints, LifeRemains, ScoreDate;
- (void)encodeWithCoder:(NSCoder *)encoder
{
//Encode properties, other class variables, etc
[encoder encodeObject:self.Username forKey:kScoreUsername];
[encoder encodeObject:self.TotalPoints forKey:kScoreTotalPoints];
[encoder encodeObject:self.LifeRemains forKey:kScoreLifeRemains];
[encoder encodeObject:self.ScoreDate forKey:kScoreDate];
}
- (id)initWithCoder:(NSCoder *)decoder
{
self = [super init];
if( self != nil )
{
//decode properties, other class vars
self.Username = [decoder decodeObjectForKey:kScoreUsername];
self.TotalPoints = [decoder decodeObjectForKey:kScoreTotalPoints];
self.LifeRemains = [decoder decodeObjectForKey:kScoreLifeRemains];
self.ScoreDate = [decoder decodeObjectForKey:kScoreDate];
}
return self;
}
Happy Coding...
I know this can be done with many languages, but I don't see how to do it using Objective-C. I've read about singletons but as they are designed to be instanciated only once, they do not feed this need.
So this class could be called like this :
MyClass* obj1 = [[MyClass alloc] initWithKey:#"oneKey"];
NSString* lib = obj1.lib;
or
int id = [MyClass idForKey:#"anotherKey"];
I've tried this code but I'm pretty sure it's really bad, but I don't see how to achieve this :
.h file
#interface MyClass : NSObject {
NSString* key;
}
#property(nonatomic, retain) NSString* key;
#property(nonatomic, readonly) int id;
#property(nonatomic, readonly) NSString* lib;
#property(nonatomic, readonly) int value;
+ (id) classWithKey:(NSString*)theKey;
#end
.m file
#import "MyClass.h"
#interface MyClass.h (Private)
-(id)initWithKey:(NSString*)theKey;
#end
#implementation MyClass
#synthesize key;
static NSMutableDictionary* vars = nil;
-(id)init
{
if (!(self = [super init])) return nil;
self.key = nil;
[MyClass initVars];
return self;
}
-(id)initWithKey:(NSString*)theKey
{
if (!(self = [super init])) return nil;
self.key = theKey;
[MyClass initVars];
return self;
}
+ (id) classWithKey:(NSString*) theKey
{
return [[[MyClass alloc] initWithKey:theKey] autorelease];
}
+(void)initVars
{
if (vars != nil) return;
#define mNum(x) [NSNumber numberWithInt:x]
#define k0 #"id"
#define k1 #"lib"
#define k2 #"val"
vars = [NSMutableDictionary dictionary];
[vars setObject:[NSDictionary dictionaryWithObjectsAndKeys:mNum(5), k0, #"One value", k1, mNum(0), k2, nil] forKey:#"oneKey"];
[vars setObject:[NSDictionary dictionaryWithObjectsAndKeys:mNum(8), k0, #"Another value", k1, mNum(1), k2, nil] forKey:#"anotherKey"];
...
[vars retain];
}
- (int)id { return [[[vars objectForKey:self.key] objectForKey:k0] intValue]; }
- (NSString*)lib { return [[vars objectForKey:self.key] objectForKey:k1]; }
- (int)value { return [[[vars objectForKey:self.key] objectForKey:k2] intValue]; }
-(void)dealloc
{
self.key = nil;
[vars release];
[super dealloc];
}
+(int) idForKey:(NSString*)theKey
{
if (vars == nil) [self initVars];
return [[[vars objectForKey: theKey] objectForKey:k0] intValue];
}
#end
take a look at singleton class concept
there are a lot of answer for singletons, just search
here's' one:
Is this really a singleton?
How to serialize the following class in objective-c so that it can be used with SBJson?
I get "JSON serialisation not supported for Animal" error when I use this code.
Can someone point out where I am going wrong?
The contents of Animal.h file is as below
#import <UIKit/UIKit.h>
#interface Animal : NSObject<NSCoding> {
NSString *name;
NSString *description;
NSString *imageURL;
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSString *description;
#property (nonatomic, retain) NSString *imageURL;
-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u;
#end
The contents of Animal.m file is as below
#import "Animal.h"
#implementation Animal
#synthesize name, description, imageURL;
-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u {
self.name = n;
self.description = d;
self.imageURL = u;
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if(self = [super initWithCoder:aDecoder])
{
name = [[aDecoder decodeObjectForKey:#"name"] retain];
description = [[aDecoder decodeObjectForKey:#"description"] retain];
imageURL = [[aDecoder decodeObjectForKey:#"imageURL"] retain];
}
return [self initWithName:name description:description url:imageURL];
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[super encodeWithCoder:encoder];
[encoder encodeObject:name forKey:#"name"];
[encoder encodeObject:description forKey:#"description"];
[encoder encodeObject:imageURL forKey:#"imageURL"];
}
#end
Make your custom class conform to NSCoding protocol and then serialize it.
For more info, visit the Apple documentation
Also, this link will also help you.
As suggested in this link, archive your custom class to NSData and serialize that as provided in the Apple documentation.
Edit
Make your Animal.m as follows:
#import "Animal.h"
#implementation Animal
#synthesize name, description, imageURL;
-(id)initWithName:(NSString *)n description:(NSString *)d url:(NSString *)u {
self = [super init];
if( self )
{
self.name = n;
self.description = d;
self.imageURL = u;
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if( self )
{
self.name = [aDecoder decodeObjectForKey:#"name"];
self.description = [aDecoder decodeObjectForKey:#"description"];
self.imageURL = [aDecoder decodeObjectForKey:#"imageURL"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeObject:name forKey:#"name"];
[encoder encodeObject:description forKey:#"description"];
[encoder encodeObject:imageURL forKey:#"imageURL"];
}
#end
To actually answer your question on how to do it using SBJson: Implement the proxyForJson method. Unless you are serializing an NSArray or NSDictionary you must override this method for serialization to work.
You can see that this is the case by looking at the SBJson source code (in SBJsonWriter.m):
- (NSData*)dataWithObject:(id)object {
...
if ([object isKindOfClass:[NSDictionary class]])
ok = [streamWriter writeObject:object];
else if ([object isKindOfClass:[NSArray class]])
ok = [streamWriter writeArray:object];
else if ([object respondsToSelector:#selector(proxyForJson)])
return [self dataWithObject:[object proxyForJson]];
else {
self.error = #"Not valid type for JSON";
return nil;
}
...
}
}
Implement proxyForJson in Animal.m like this (not tested):
- (NSDictionary*) proxyForJson
{
return [NSDictionary dictionaryWithObjectsAndKeys:self.name, #"name",
self.description, #"description",
self.imageURL, #"imageURL",
nil];
}
This open source project JSONCoding makes the whole process pretty simple, using the new sdk class in conjunction with the NSCoding protocol.
Check the newly introduced NSJSONSerialization class:
NSJSONSerialization class
I think you can check out this if it helps you: Make a Custom Class Serializable
Please check this Property List Programming Guide - Serializing a Property List
and the similar post here:
Make a Custom Class Serializable in Objective-c/iPhone?
Object serialization in XML format using Obj-C / iPhone SDK
See also https://github.com/jsonmodel/jsonmodel
Magical Data Modeling Framework for JSON - allows rapid creation of
smart data models. You can use it in your iOS, macOS, watchOS and tvOS
apps.
This is also the chosen library for the Objective-c Swagger client.
I am developing iphone application.
I use NSCoder.
MyApplication.h
#define ITEMS_KEY #"items"
#define CATEGORIES_KEY #"categories"
#import <UIKit/UIKit.h>
#interface MyApplicationData : NSObject <NSCoding, NSCopying> {
NSMutableArray* items;
NSMutableArray* categories;
}
#property (nonatomic ,retain) NSMutableArray* items;
#property (nonatomic, retain) NSMutableArray* categories;
#end
Myapplication.m
#import "MyApplicationData.h"
#implementation MyApplicationData
#synthesize items;
#synthesize categories;
#pragma mark NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:items forKey:ITEMS_KEY];
[aCoder encodeObject:categories forKey:CATEGORIES_KEY];
}
-(id)initWithCoder:(NSCoder *)aDecoder{
if(self = [super init]){
self.items = [aDecoder decodeObjectForKey:ITEMS_KEY];
self.categories = [aDecoder decodeObjectForKey:CATEGORIES_KEY];
}
return self;
}
#pragma mark -
#pragma mark NSCopying
-(id)copyWithZone:(NSZone *)zone{
MyApplicationData* copy = [[[self class]allocWithZone:zone]init];
items = [self.items copy];
categories = [self.categories copy];
return copy;
}
#end
But warnning.
'NSCoder' may not respond to '-decodeDataObjectForKey'
How to use NSCoder?
Use -decodeObjectForKey: and read the documentation.
Here is the NSCoding protocal methods from my LogEntry object, you can ignore the switch statement and the schema details though, those are from a base class I have written that allows me to keep sane track of changing data formats.
Please note that in addition to using decodeObjectForKey: you also need to make sure you retain/copy the given values as they are autoreleased when received.
- (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self != nil) {
switch ([schemaVersion intValue]) {
case 2:
filepath = [[coder decodeObjectForKey:#"filepath"] copy];
identifier = [coder decodeInt64ForKey:#"identifier"];
level = [coder decodeIntForKey:#"level"];
lineNumber = [[coder decodeObjectForKey:#"lineNumber"] retain];
message = [[coder decodeObjectForKey:#"message"] retain];
timestamp = [[coder decodeObjectForKey:#"timestamp"] retain];
break;
default:
[self release], self = nil;
break;
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:filepath forKey:#"filepath"];
[coder encodeInt64:identifier forKey:#"identifier"];
[coder encodeInt:level forKey:#"level"];
[coder encodeObject:lineNumber forKey:#"lineNumber"];
[coder encodeObject:message forKey:#"message"];
[coder encodeObject:timestamp forKey:#"timestamp"];
[super encodeWithCoder:coder];
}
I think you should be using -decodeObjectForKey:
I wrote a helper function for using NSCoding. It's a part of VSCore Library. Check it out here:
#interface QuickCoding : NSObject
+ (void)quickEncode:(NSObject<NSCoding>*)object withEncoder:(NSCoder*)encoder;
+ (void)quickDecode:(NSObject<NSCoding>*)object withDecoder:(NSCoder*)decoder;
#end
And the .m file:
#import "QuickCoding.h"
#import "ReflectionHelper.h"
#define QUICK_CODING_HASH #"h4"
#implementation QuickCoding
+ (void)quickEncode:(NSObject<NSCoding>*)object withEncoder:(NSCoder *)encoder{
NSArray *codingKeys = [ReflectionHelper fieldsList:[object class]];
NSUInteger hash = [[codingKeys componentsJoinedByString:#""] hash];
[encoder encodeObject:#(hash) forKey:QUICK_CODING_HASH];
[codingKeys enumerateObjectsUsingBlock:^(NSString *key, __unused NSUInteger idx, __unused BOOL *stop) {
id val = [object valueForKey:key];
if ([val conformsToProtocol:#protocol(NSCoding)]){
[encoder encodeObject:val forKey:key];
}
}];
}
+ (void)quickDecode:(NSObject<NSCoding>*)object withDecoder:(NSCoder *)decoder{
NSArray *codingKeys = [ReflectionHelper fieldsList:[object class]];
NSUInteger hash = [[codingKeys componentsJoinedByString:#""] hash];
NSUInteger decodedHash = [[decoder decodeObjectForKey:QUICK_CODING_HASH] unsignedIntegerValue];
BOOL equalHash = hash == decodedHash;
[codingKeys enumerateObjectsUsingBlock:^(NSString *key, __unused NSUInteger idx, __unused BOOL *stop) {
id val = [decoder decodeObjectForKey:key];
if (equalHash || val){
[object setValue:val forKey:key];
}
}];
}
#end
Full code is here: https://github.com/voipswitch/VSCore/tree/master/VSCore/Storage