How to make my singleton class extendable?

泄露秘密 提交于 2019-12-10 18:00:46

问题


We have a singleton class in one of our static libraries. It is a singleton because we want to be able to "remember" its state at all times. In essence it is a User management singleton.

It has a property User *user and it has methods such as - (void)authenticateUser.

We want to deliver this to a client who will want to create their own - (void)authenticateUser method. To do this I envisioned they would extend the UserManager class and just override the method.

However as it is a singleton it has methods like: - (id)sharedInstance and a custom alloc method. They all look like this:

static UserManager *_sharedInstance = nil;

+ (id)sharedInstance {
    @synchronized([UserManager class]) {
        if(!_sharedInstance){
            id temp = [[self alloc] init];
            [temp class];
        }
        return _sharedInstance;
    }
    return nil;
}

+ (id)alloc {
    @synchronized([UserManager class]) {
        NSAssert(_sharedInstance == nil, @"Attempted to allocate a second instance of a singleton");
        _sharedInstance = [super alloc];
        return _sharedInstance;
    }
    return nil;
}

So given this, is it possible to subclass and extend this UserManager class? Can I create a ChildUserManager which overrides a function? Or will I have to rewrite these singleton methods to deal with the "new" child class?

Is there any way that I can modify these methods to make it so that 3rd parties can easily extend this class?

Thanks


回答1:


Beter design would be to use Composition then Inheritance. Declare a protocol AuthenticationDelegate

@protocol AuthenticationDelegate
@optional
-(void) authenticateUser:(User*)inUser;
@end

Have a property in UserManager that by default points to UserManager.

@class UserManager : NSObject <AuthenticationDelegate> {
    ......
} 

@property (assign) id<AuthenticationDelegate>  authenticator

+ (id)sharedInstance;

Your clients if they want to authenticate by their method then they must confirm themselves to AuthenticationDelegate protocol and implement its methods. They must set authenticator property to their class that confines to . However its singleton. So they can set it as soon as their object is instantiated. Hence they can use their Authenticator.

But make it sure that authenticator dosent point to nil. You can implement the setAuthenticator: method so that authenticator will be pointing to UserManager when clients set this to nil.

static UserManager *_sharedInstance = nil;

@implementation UserManager

@synthasize authenticator;

+ (id)sharedInstance {
    @synchronized([UserManager class]) {
        if(!_sharedInstance){
            id temp = [[self alloc] init];
            [temp class];
        }
        return _sharedInstance;
    }
    return nil;
}

+ (id)alloc {
    @synchronized([UserManager class]) {
        NSAssert(_sharedInstance == nil, @"Attempted to allocate a second instance of a     singleton");
        _sharedInstance = [super alloc];
        return _sharedInstance;
    }
    return nil;
}

-(void)init {
    self = [super init];
    if (self) {
        self.authenticator = nil;
    }
}

-(void)setAuthenticator:(id<AuthenticationDelegate>)inAuthenticator {
    if (!inAuthenticator) {
        __authenticator = self;
    } else {
        __authenticator = inAuthenticator;
    }
}

#pragma mark - AuthenticationDelegate

-(void) authenticateUser:(User*)inUser
{
    // Your Authentication Code.
}

Hope this helps...




回答2:


A better singleton pattern:

// UserManager.h

#import <Foundation/Foundation.h>

@interface UserManager : NSObject

+(instancetype)sharedInstance;

// clue for improper use (produces compile time error)
+(instancetype) alloc __attribute__((unavailable("alloc not available, call sharedInstance instead")));
-(instancetype) init  __attribute__((unavailable("init not available, call sharedInstance instead")));
+(instancetype) new   __attribute__((unavailable("new not available, call sharedInstance instead")));

@end


// UserManager.m

#import "UserManager.h"

@implementation UserManager

+(instancetype) sharedInstance {
    static dispatch_once_t pred;
    static id shared = nil;
    dispatch_once(&pred, ^{
        shared = [[super alloc] initUniqueInstance];
    });
    return shared;
}

-(instancetype) initUniqueInstance {
    return [super init];
}

@end

If you subclass, the single instance will be set to whatever is called first, eg:

[Animal sharedInstance];    // instance set to Animal
[[Cow sharedInstance] moo]; // unknown selector 'moo' because the instance is Animal

This alone would work tho:

[[Cow sharedInstance]moo];


来源:https://stackoverflow.com/questions/16102888/how-to-make-my-singleton-class-extendable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!