0

I have the following situation, i can't resolve:

@interface Deck : NSObject
@interface MasterDeck : Deck 
@interface PlayerDeck : Deck

Inside MasterDeck class, as part of initialization, i call

[self cutDeckImageIntoCards]; // We don't get to execute this method

Call results in an error [PlayerDeck cutDeckImageIntoCards]: unrecognized selector sent to instance

Indeed, PlayerDeck does not have this method .. but why is it being called at all?

After looking at MasterDeck's initialization i added a few debugging statements:

static MasterDeck *gInstance = NULL;

+(MasterDeck *) instance {
    @synchronized(self) {
        if (gInstance == NULL) {
            gInstance = [[self alloc] init];
        }
    }

    return gInstance;
}

-(id) init {

    if (gInstance != NULL) {
        return gInstance;
    }

    // MasterDeck
    self = [super init];  
    // PlayerDeck

    if (self) {
       // Lots of stuff
       [self cutDeckImageIntoCards]
       // Some more stuff
     }

    gInstance = self;
    return gInstance;
}

Ok, so MasterDeck is PlayerDeck because' Deck thinks it is a PlayerDeck ... Deck confirms

Deck is created as follows:

static Deck *gInstance = NULL;

+(Deck *) instance {
    @synchronized(self) {
        if (gInstance == NULL) {
            gInstance = [[self alloc] init];
        }
    }

    return gInstance;
}

-(id) init {

    if (gInstance != NULL) {
        return gInstance;
    }

    self = [super init];

    if (self) {               
       // Do something
    }

    NSLog(@"Deck thinks it's a %@", [[self class ]description]); // PlayerDeck

    gInstance = self;
    return gInstance;
}

So, again

  • @interface Deck : NSObject

Assuming above Singleton Implementation, why would Deck think it's actually a PlayerDeck?

James Raitsev
  • 92,517
  • 154
  • 335
  • 470
  • Show the code that created this object in question 'self'. You must have inadvertently created an instance of PlayerDeck – Firoze Lafeer Dec 09 '11 at 02:51
  • What happens if you change MasterDeck to subclass NSObject directly and independently implement what was the super's init method? If the problem goes away, that might give you a clue. Maybe Deck's init method mistakenly instantiates PlayerDeck. – Wienke Dec 09 '11 at 03:06
  • @Wienke, I thought about it, does not seem that way .. I posted Deck's initialization code here as well – James Raitsev Dec 09 '11 at 03:07

4 Answers4

3

So the way you've written this, if you create the PlayDeck instance first, then the Deck instance is now a PlayDeck.

And then if you go to create the MasterDeck instance, your call to [super init] dutifully returns that previous PlayDeck instance.

So why is Deck a singleton at all? Deck has two subclasses that are singletons, but are you really looking for a singleton Deck also?

At a minimum, you can make this sort of work by not setting gInstance from within each init. Let the class method do that. Just return self from each of the init's. Also, remove the check for gInstance being not null, other Deck's init will always return Deck's instance once you have an instance of Deck.

But beyond that, I would rethink this idea a bit. Hope that helps.

Firoze Lafeer
  • 17,133
  • 4
  • 54
  • 48
2

You'll probably want to separate your singleton class from the actual class.

Try implementing it as in this example,

+(id) instance {
    static dispatch_once_t pred;
    static MasterDeck *sharedInstance = nil;
    dispatch_once(&pred, ^{
        sharedInstance = [[MasterDeck alloc] init];
    });
    return sharedInstance;
}
Community
  • 1
  • 1
john
  • 3,043
  • 5
  • 27
  • 48
1

What happens if you replace [[self alloc] init] with [[MasterDeck alloc] init]?

It may be that somehow self is PlayerDeck. To make sure, you could NSLog([self description]) just before calling + alloc.

Edit

I assume that the interesting part of the code you have above is part of the @implementation of MasterDeck. My suggestion would be to try a lot more logging, including determining what super and [self class] are before calling [super init], although these may be misleading...

Also, as a side note, I believe that you should call [self release] in init if you are returning the previously-created instance.

What does the [super init] method look like? Can you step into it, or is it the default initializer?

Edit 2

I think you're doing singletons wrong. If you initialize a PlayerDeck, that would create a singleton in Deck which is an instance of PlayerDeck. Then later, when you initialize a MasterDeck, calling [super init] will return the instance already created by the PlayerDeck.

Ed Marty
  • 39,590
  • 19
  • 103
  • 156
  • All the way until i `[super init]` `MasterDeck` is `MasterDeck`, past that line it becomes a `PlayerDeck` – James Raitsev Dec 09 '11 at 03:06
  • Ed, in super, `NSLog(@"Deck thinks it's a %@", [[self class ]description]);` prints PlayerDeck. This would explain why MasterDeck thinks it's a PlayerDeck, but now ... why does Deck thinks it's a PlayerDeck? – James Raitsev Dec 09 '11 at 03:15
  • Where specifically are you first calling `+instance` (or `-init` if you are calling that instead)? What does the line look like? – Ed Marty Dec 09 '11 at 03:17
1

It looks like you try to be clever, but fact is - often the computer is even smarter. :)

Your deck class caches an instance in gInstance - in fact, it looks like it may store a Deck, a PlayerDeck, or a MasterDeck, depending on what and how you call / instantiate first. After that, this very instance is returned by that init method.

I strongly suggest to get this code clean and readable. I bet there are numerous problems with this code - but your problem is already a good example. Your logic (which should be simple, I guess) can surely be implemented much easier.

Note - I'm not against singletons, but this sort of code stacking is an absolute no-go. It's hard to get more dependency logic into those lines. ;)

Eiko
  • 25,601
  • 15
  • 56
  • 71