问题
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