NSCoding and ARC - iphone

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?

Related

Accessing changeable values in a singleton?

First off, I come from Lua, don't blame me for being global variable minded lol. So, I've been reading up on how to use this whole "Singleton system" and I'm not sure if I'm completely missing the point or if I'm just implementing it incorrectly?
The goal of my code is to create a way for multiple files to access a variable that holds the size of an array in a specific file. Here is my singleton:
.h
#import <Foundation/Foundation.h>
#interface GlobalVariables : NSObject
{
NSNumber *currentGameArrayCount;
BOOL *isGamePaused;
}
#property (nonatomic, readwrite) NSNumber *currentGameArrayCount;
#property (nonatomic, readwrite) BOOL *isGamePaused;
+ (GlobalVariables *)sharedInstance;
#end
.m
#import "GlobalVariables.h"
#implementation GlobalVariables
#synthesize currentGameArrayCount, isGamePaused;
static GlobalVariables *gVariable;
+ (GlobalVariables *)sharedInstance
{
if (gVariable == nil) {
gVariable = [[super allocWithZone:NULL] init];
}
return gVariable;
}
- (id)init
{
self = [super init];
if (self)
{
currentGameArrayCount = [[NSNumber alloc] initWithInt:0];
isGamePaused = NO;
}
return self;
}
#end
and in another file with the array I use:
GlobalVariables *sharedData = [GlobalVariables sharedInstance];
NSNumber *tmpArrayCount = [sharedData currentGameArrayCount];
NSInteger tmpCount = [whereStuffActuallyHappens.subviews count]; // Subviews is the array
NSNumber *currentCount = [NSNumber numberWithInteger:tmpCount];
tmpArrayCount = currentCount;
the hope of this code was to get the variable in the singeton (currentGameArrayCount) and set it too what the current array count was (currentCount). Am I incorrectly interpreting the purpose of a singleton? Am I just bad at singletons and didn't set it up correctly? Does anyone know how I could achieve the result of getting my array count to be accesible to all my files?
You have a few issues. Try these changes:
GlobalVariables.h:
#import <Foundation/Foundation.h>
#interface GlobalVariables : NSObject
#property (nonatomic, assign) int currentGameArrayCount;
#property (nonatomic, assign) BOOL gamePaused;
+ (GlobalVariables *)sharedInstance;
#end
GlobalVariables.m:
#import "GlobalVariables.h"
static GlobalVariables *gVariable = nil;
#implementation GlobalVariables
+ (GlobalVariables *)sharedInstance {
if (gVariable == nil) {
gVariable = [[self alloc] init];
}
return gVariable;
}
- (id)init {
self = [super init];
if (self) {
self.currentGameArrayCount = 0;
self.gamePaused = NO;
}
return self;
}
#end
Now in your other code you can do:
GlobalVariables *sharedData = [GlobalVariables sharedInstance];
int tmpArrayCount = sharedData.currentGameArrayCount;
NSInteger tmpCount = [whereStuffActuallyHappens.subviews count]; // Subviews is the array
sharedData.currentGameArrayCount = tmpCount;

Encode Decode int attribute of a class error

#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.

initWithCoder for loading xib example [duplicate]

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...

Not Able to Add Object to NSMutableArray

Hey guys, I have this code within a function inside a class that is a subclass of NSOperation:
//...
#implementation DataLoader
#synthesize addedAnnotations;
#synthesize addedOverlays;
#synthesize loaderFunc;
#synthesize DLDelegate;
//...
-(id)initWithFunction:(LoaderFunc)func withDelegate:(id)delegate {
if (self = [super init]) {
self.addedOverlays = nil;
self.addedAnnotations = nil;
self.loaderFunc = func;
self.DLDelegate = delegate;
return self;
}
return nil;
}
//...
//inside a function
for (ParkingAnnotations *annotation in fetchedObjects) {
ParkingAnnotation *parkingAnnot = [[ParkingAnnotation alloc] init];
workingCoordinate.latitude = [[annotation latitude] doubleValue];
workingCoordinate.longitude = [[annotation longitude] doubleValue];
[parkingAnnot setCoordinate:workingCoordinate];
[parkingAnnot setTitle:[annotation valueForKey:#"lotName"]];
[parkingAnnot setAnnotationType:[annotation iconTypeRaw]];
[self.addedAnnotations addObject:parkingAnnot];//parkingAnnot not added to array here
[parkingAnnot release];
}
//...
Added annotations is an NSMutable array, I have been walking through this code with the debugger and for some reason the parkingAnnot object is not getting added to the array. Here is the relevant header code for the class:
//...
#interface DataLoader : NSOperation {
NSMutableArray *addedAnnotations;
NSMutableArray *addedOverlays;
LoaderFunc loaderfunc;
id <DataLoaderProtocol> DLDelegate;
}
#property (nonatomic, retain) NSMutableArray* addedAnnotations;
#property (nonatomic, retain) NSMutableArray* addedOverlays;
#property (nonatomic) LoaderFunc loaderFunc;
#property (assign) id DLDelegate;
//...
It is an astonishing problem because the function in which I am experiencing the problem was copied from my MapViewController and is essentially the same, but instead of mapView addAnnotation: I am adding to an NSMutable array instead. Any idea of what's up? Thanks in advance!
Where are you actually instantiating the addedAnnotations array? I only see it being assigned nil in your initialize function, maybe it should change to something like:
self.addedAnnotations = [[[NSMutableArray alloc] init] autorelease];

invalid CFArrayRef problem with Singleton object

I've built a singleton object to manage some data in my app
#interface MyCommon : NSObject {
NSArray *quizz;
int iCurrentQuestion;
};
+ (MyCommon *)singleton;
#property (retain) NSArray *quizz;
#property (assign) int iCurrentQuestion;
#end
MyCommon.m
#import "MyCommon.h"
// MyCommon.m:
#implementation MyCommon
static MyCommon * MyCommon_Singleton = nil;
#synthesize iCurrentQuestion;
+ (MyCommon *)singleton
{
if (nil == MyCommon_Singleton)
{
MyCommon_Singleton = [[MyCommon alloc] init];
NSLog(#"allocating MyCommon_Singleton at %#",MyCommon_Singleton);
}
else {
NSLog(#"accessing singleton : %#", MyCommon_Singleton);
}
return MyCommon_Singleton;
}
- (NSArray*) getQuizz{
return quizz;
}
- (void) setQuizz:(NSArray *)array {
quizz = [NSArray arrayWithArray:array];
NSLog(#"setQuizz : %#",quizz);
}
There is no problem for writing the quizz object (setQuizz), however when I try to access it for reading, I get a crash : the quizz looks invalid and Xcode notify me an invalid CFArrayRef
I don't know what's wrong with my code.
You provide a custom setter for quizz but it doesn't comply with how the property is declared.
You're not retaining quizz when you're setting a new value. It's likely to be released just after, leading to a crash when you access it.
You should write
- (void)setQuizz:(NSArray *)array {
if (quizz != array) {
NSArray *tmp = quizz;
quizz = [array retain]; // retain the new value
[tmp release]; // release the old one
}
NSLog(#"setQuizz : %#",quizz);
}
this is way more code than it needs to be. First if you are going to be providing your own method you should declare so in the #property declaration which you didn't. Also your not properly retaining your variables. Additionally you should be using dispatch_once() for a thread safe & fast way to guarantee the singleton is only created once.
#interface MyCommon : NSObject {}
#property(nonatomic, retain) NSArray *quiz;
#property (assign) int iCurrentQuestion;
+ (MyCommon *)singleton;
#end
#implementation MyCommon
#synthesize quiz;
#synthesize iCurrentQuestion;
-(id)init {
self = [super init];
if(self) {
quiz = [[NSMutableArray alloc init];
iCurrentQuestion = 0;
}
return self;
}
+ (MyCommon *)singleton {
static MyCommon *singleton = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
singleton = [[MyCommon alloc] init];
});
return singleton;
}
#end
then you just do
[MyCommon singleton].quiz = //some array