Reputation: 5246
Since it doesn't make sense for my app to have more than one LoginViewController
, I tried to combine the singleton pattern with initWithNibName like so:
+ (instancetype)sharedInstance {
static id sharedInstance;
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [super initWithNibName:@"LoginViewController" bundle:nil];
});
return sharedInstance;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
NSLog(@"ignoring initWithNibName and calling sharedInstance");
// self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
// if (self) {
//
// }
return [LoginViewController sharedInstance];
}
The problem is that the line:
sharedInstance = [super initWithNibName:@"LoginViewController" bundle:nil];
Gives me this error:
No known class method for selector 'initWithNibName:bundle:'
So how could this be the case if the exact same code would normally be called in initWithNibName:bundle
? I suspect it's because sharedInstance is a static method and that UIViewController doesn't have a static method for initWithNibName:bundle
.
Still, I'm wondering if there is a way around it because I prefer not to have to create a LoginViewController every time I need to use it.
I'd also like to guard against calling LoginViewController initWithNibName:bundle:
and getting a separate instance.
Upvotes: 0
Views: 2495
Reputation: 6515
+sharedInstance
is a class method, so inside it self
evaluates to the class LoginViewController
and super
refers to the class UIViewController. -initWithNibNamed:bundle:
is an instance method instead.
static LoginViewController *sharedInstance;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
if(sharedInstance) {
// avoid creating more than one instance
[NSException raise:@"bug" format:@"tried to create more than one instance"];
}
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if(self) {
// own initialization code here
}
return self;
}
+ (LoginViewController *)sharedInstance
{
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [[LoginViewController alloc] initWithNibName:@"LoginViewController" bundle:nil];
});
return sharedInstance;
}
there is an implicit contract with the -init
method family that says that alloc+init should always return a new object or one that is semantically indistinguishable from a new object. E.g. [NSNumber numberWithInt:2]
doesn't necessarily have to return always a new object, because there is no need to distinguish between two different 2's.
Therefore, you have two options: 1) raise an exception if someone tries to create an additional LoginViewController
or 2) allow creating additional LoginViewController
s. The third option ( 3) just return the sharedInstance if someone tries to create a new LoginViewController ) breaks the contract of initialization, which means it will return something to the caller, that he is not expecting! If the caller is aware that LoginViewController is a singleton, he should use +sharedInstance
. If the caller is not aware of it, he should either get an exception or get a new LoginViewController.
Upvotes: 4