2

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"];
}
Community
  • 1
  • 1
oky_sabeni
  • 7,672
  • 15
  • 65
  • 89

5 Answers5

2

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.

martiall
  • 981
  • 6
  • 7
  • Can you elaborate on how to move the code inside the Singleton init function? I will update my question with my Singleton class code. Thanks. – oky_sabeni Jun 29 '11 at 17:01
2

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"];
YuzaKen
  • 136
  • 9
2

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.

Eiko
  • 25,601
  • 15
  • 56
  • 71
  • I am sorry but I do not understand your answer. I did not instantiate my Singleton from nibs. In my mind, the Singleton class is an NSObject which is an object analogous to a NSMutableArray. So, when application suspends, archieve is called [archieve encodeOBject:singleton forKey:@"singleton"] which is why I am using the key "singleton" to unarchieve the singleton object. Can you explain more elaborately if my logic is flawed? Thanks. – oky_sabeni Jun 29 '11 at 16:58
  • @Yko. Ah, that's your intention. I would suggest to save the state of the singleton when the application supsends and load when resuming. I'd put that logic in the Singleton, i.e. save/restore methods that use a keyed archiever. You approach seem to load another instance after already instantiating one - hence messing up with the pattern. – Eiko Jun 29 '11 at 22:02
  • Thanks. I'll look into that. It certainly makes more sense to simply archieve the one and only one singleton object and when the app starts then simply bring that one up. I'll look into that and improve my code a bit. – oky_sabeni Jun 30 '11 at 15:54
1

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.

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • True. I agree with you and the rest of the smart people and I want to create only 1 instance of the Singleton class. But, how do I do that? I use a Singleton for a "Settings" variable which should last the entire app lifetime and work throughout the entire app so I think it fits the Singleton model. And now, I want to archive it so that the next time the app starts up, it will remember that "Setting". That is all I want really. Interestingly, in another app where I archive only one Singleton object using archieveRootObject, everything seems to work. – oky_sabeni Jun 29 '11 at 18:16
  • @Yko, the root of the problem is that you're trying to replace your shared Singleton instance. That's hard to do right with a true singleton, because it's difficult to know if any objects might have stored a reference to the original instance. Have you considered ditching your Singleton class and using the real singleton that iOS provides to your app specifically for recording settings information? NSUserDefaults seems to be just what you're trying to reinvent. – Caleb Jun 29 '11 at 18:36
  • Ahh Yes. I did look around at NSUserDefaults but I figured I can probably use my own Singleton class and make it easier in case I need to add more stuff. And somehow using the NSUserDefaults kinda confuse and annoy me and I think it has something to do with create a .plist file and after you change in code, the value in the plist file does not change or something like that. In any case, it becomes kinda confusing for me when the default is changed or not... – oky_sabeni Jun 30 '11 at 15:53
0

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.

oky_sabeni
  • 7,672
  • 15
  • 65
  • 89