bertday
bertday

Reputation: 10971

Objective-C singleton only responds to class methods

I have a singleton class that is instantiated as follows:

#import "FavoritesManager.h"

static FavoritesManager *sharedFavoritesManager = nil;

@implementation FavoritesManager

+ (id)sharedManager {
    @synchronized(self) {
        if (sharedFavoritesManager == nil) {
            sharedFavoritesManager = [[self alloc] init];
        }
    }

    return self;
}

This returns an object, but for some reason it will only respond to class methods. If I call a instance method I get

+[FavoritesManager testMethod]: unrecognized selector sent to class 0x59198

For what it's worth, this is what testMethod looks like:

- (void)testMethod {
    NSLog(@"Test");
}

and I'm absolutely positive it's declared in the interface. I've used this exact code in other classes and it works like a charm, so I don't really understand what the problem is here. One thing that is suspicious is the plus sign in +[FavoritesManager testMethod], but I can't explain it. Any ideas?

EDIT: I was confusing public/private and class/method terminology. Thanks to everyone who pointed that out.

Upvotes: 0

Views: 2635

Answers (7)

jscs
jscs

Reputation: 64002

The error indicates that you're sending the message testMethod to the class, rather than an instance.

The reason for this is that your sharedManager method is incorrect. You are currently returning self, which, in this class method, is the class itself. This means that when you write [[FavoritesManager sharedManger] testMethod] you end up sending testMethod to the class. Since testMethod isn't a class method, you get an error.

You should have return sharedFavoritesManager; at the end of sharedManager, not return self;. The latter is correct only in instance method initializers.

Also, as dbrajkovic commented, you seem to be confused about public/private and class/instance methods. Strictly, ObjC has no private methods. You can hide the declaration, which will cause a compiler warning, but the message will still be sent and the method will be called. The + and - distinguish class methods from instance methods; the distinction is which kind of object you send a message to. Info here: What is the difference between class and instance methods?

Upvotes: 4

Tommy
Tommy

Reputation: 100622

If you want to call testMethod from another class method then you need:

+ (void)testMethod {
    NSLog(@"Test");
}

The reason is that if you call a class method then there's no instance, so nothing on which to call instance methods. But probably you want to call:

[[FavoritesManager sharedManager] testMethod];

Which means 'get the shared instance, then call testMethod on it'. Thinking as I type, you might also like to add:

+ (void)forwardInvocation:(NSInvocation *)anInvocation
{
    id sharedManager = [self sharedManager];

    if ([sharedManager respondsToSelector:
            [anInvocation selector]])
        [anInvocation invokeWithTarget:sharedManager];
    else
        [super forwardInvocation:anInvocation];
}

Which is the Objective-C means for message forwarding. So if the metaclass FavoritesManager receives a message it can't respond to, it lets its shared manager instance have a go. That means that:

[FavoritesManager testMethod];

Becomes functionally equivalent to (though a little slower than):

[[FavoritesManager sharedManager] testMethod];

Providing that you haven't implemented a class method in addition to an instance method. You can learn more about message forwarding in Apple's official documentation.

Upvotes: 6

peterept
peterept

Reputation: 4427

Call your singleton instance:

[[ FavoritesManager sharedManager] testMethod];

Upvotes: 0

dbrajkovic
dbrajkovic

Reputation: 3703

The error is right you must be calling [FavoritesManager testMethod] which means you're trying to call a class method. I believe you want [[FavoritesManager sharedManager] testMethod];

Upvotes: 2

SuperRod
SuperRod

Reputation: 557

Instead try

[[FavoritesManager sharedFavoritesManager] testMethod];

Upvotes: 1

calimarkus
calimarkus

Reputation: 9977

there are no priavte methods in obj-c. But anyway on a singleton you are always calling from the outside of the class, so only declare "public methods". for detailed help post your code.

Upvotes: 0

QED
QED

Reputation: 9923

+ at the start of a method declaration means that it's a class method, - means that it's an instance method. Do this:

+(void)testMethod {
    NSLog(@"Test");
}

If you want to invoke testMethod on your sharedManager, then keep the testMethod declaration as you have it and instead change your invocation to

[[FavoritesManager sharedFavoritesManager] testMethod];

Either will work, and choosing between the two is a matter of app design.

Upvotes: 1

Related Questions