2
votes

Following this link: How do I archive two objects using NSKeyedArchiever? I am able to archieve an array and a singleton object. I can archieve and correctly unarchieve it at didFinishLaunchingWithOptions.

However, my singleton object does not persist past the app delegate. In the next view controller, the singleton variables goes back to its default value even though in didFinishLaunchingWithOptions, I did a NSLog and verified. Here is an example,

In application didFinishLaunchingWithOptions:

Singleton *singleton = [Singleton sharedSingleton];
NSData *data = [NSData dataWithContentsOfFile:path];
    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
singleton = [unarchiver decodeObjectForKey:@"singleton"];
[unarchiver finishDecoding];

NSLog(@"singleton sorting after decode: %@", [singleton sort]); // ouput: alphabet

Then, I alloc/init a viewController and set it as root view controller for my navigation controller:

navController = [[UINavigationController alloc] initWithRootViewController:viewController];

In viewController -> viewWillAppear,

I call a sorting function: [self sort...];

And in the sorting function, I call the singleton class: Singleton *singleton = [Singleton sharedSingleton]; But now, NSLog(@"singleton sort: %@", [singleton sort]); // output: amount NOT alphabet

I can post my Singleton code but I know it works. In fact, in my settings view controller, if I change the [singleton sort] variable, it will persist through all view controllers.

This is confusing me as to why my singleton object does not persist from app delegate to my view controllers. Any tips/hints are appreciated.

Thanks!!!

EDIT: Singleton class implementation

In Singleton.m:

static Singleton *shared = NULL;

+ (id)sharedSingleton
{
    @synchronized(self)
    {
        if ( !shared || shared == NULL )
        {
            // allocate the shared instance, because it hasn't been done yet
            NSLog(@"Allocating Singleton...");
            shared = [[Singleton alloc] init];
        }
        return shared;
    }
}


- (id)init
{
    if ( self = [super init] )
    {
        sort = [[NSString alloc] initWithString:@"amount"];
        showTutorial = YES;
    }
    return self;    
}

-(id)initWithCoder:(NSCoder *)aDecoder
{
    [super init];
    [self setSort:[aDecoder decodeObjectForKey:@"sort"]];
    [self setShowTutorial:[aDecoder decodeBoolForKey:@"showTutorial"]];

    return self;
}

-(void)encodeWithCoder:(NSCoder *)aCoder
{
    [aCoder encodeObject:sort forKey:@"sort"];
    [aCoder encodeBool:showTutorial forKey:@"showTutorial"];
}
5
How have you implemented your initWithCoder: method?Deepak Danduprolu
@Deepak. I've updated the question to include the code for my Singleton class. Thanks so much!!!okysabeni

5 Answers

2
votes

In your code you are just updating the value of the singleton pointer in the current context and not the sharedSingleton instance

Try to create a method in your Singleton class which update the singleton class with the unarchiver object something like :

[[Singleton sharedSingleton] loadArchive:unarchiver];

or (which is better I think) move your code inside the Singleton init function.

2
votes

You've also assigned an autorelease object to a local variable. This object will become out of scope (i.e. released) next time through event loop.

singleton = [unarchiver decodeObjectForKey:@"singleton"];
2
votes

I think you got the singleton pattern wrong.

You create it in code

Singleton *singleton = [Singleton sharedSingleton];

which is right, but doing

singleton = [unarchiver decodeObjectForKey:@"singleton"];

makes no sense for a singleton at all. Generally speaking, you do not want to instantiate your Singletons from nibs.

1
votes

Your singleton pointer is just a local variable that initially points to the shared instance of Singleton. You then come along and change the value of singleton so that it points to the object that you decode from the unarchiver. Making this local pointer point to the new Singleton does not change the shared instance of Singleton, and it doesn't change the value of the shared global either.

Additionally, I think you might misunderstand the singleton pattern. The singleton pattern is supposed to be used when no more than one instance of a class can be allowed to exist. Singletons are often used because they're easy to access globally, but many smart people consider using singletons for this reason an abuse of the pattern. You can agree or disagree on this point, but the fact here is that you're creating two instances of your Singleton class, and by definition that means that your Singleton class is not a true implementation of the singleton design pattern.

0
votes

Most of the comments given below points to the right direction in that in my original code, I was creating two instances of the Singleton class. And I realize my folly so here is the code that works:

Singleton *singleton = [unarchiver decodeObjectForKey:@"singleton"];

Then, use the one and only instance of mySingleton:

Singleton *mySingleton = [Singleton sharedSingleton];
[mySingleton setSort:[singleton sort]];

So, this way, the unarchiver pulls an instance of a singleton object which is autoreleased but the value is set to the real Singleton class.

Note: some comments mentioned that I may not be using the Singleton class properly and I probably am not. I'm not expert at this. I just want to save something and have it back when the app terminates. If it helps you, great. If not, post a comment to help us all learn better. If I got better and learn more, maybe I'll update this post.

Thanks for all the comments though. They are all right. I've upvoted all of them.