My question is quite similar to this one: Use Singleton In Interface Builder?
The only difference is that I use ARC. So, if simplified, my singleton looks like that:
Manager.m
@implementation Manager
+ (instancetype)sharedManager {
__strong static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
@end
So the question is if it's possible to adopt it for Interface Builder still being with ARC?
Of course, I understand that it might be simpler just to rewrite that class without ARC so the question is rather academic. :)
When the nib is unarchived, it'll attempt to either alloc
/init
or alloc
/initWithCoder:
a new instance of the class.
So, what you could do is intercept that call and re-route it to return your singleton:
+ (id)sharedInstance {
static Singleton *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self actualAlloc] actualInit];
});
return sharedInstance;
}
+ (id)actualAlloc {
return [super alloc];
}
+ (id)alloc {
return [Singleton sharedInstance];
}
- (id)actualInit {
self = [super init];
if (self) {
// singleton setup...
}
return self;
}
- (id)init {
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
return self;
}
This allows -init
and -initWithCoder:
to be safely called multiple times on the same object. It's generally not recommended to allow this, but given that singletons are already cases of "a place where things can get really wonky", this isn't the worst you could do.
Just to be complete, here's an implementation of Singleton which might be used from Interface Builder. The difference is in actualAlloc
method. As [super alloc]
would still call [self allocWithZone:]
– it wouldn't allocate the object.
Singleton.h
@interface Singleton : NSObject
+ (instancetype)sharedInstance;
@end
Singleton.m
@implementation Singleton
+ (instancetype)sharedInstance {
__strong static id _sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self _alloc] _init];
});
return _sharedInstance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
+ (id)alloc {
return [self sharedInstance];
}
- (id)init {
return self;
}
+ (id)_alloc {
return [super allocWithZone:NULL]; //this is important, because otherwise the object wouldn't be allocated
}
- (id)_init {
return [super init];
}
@end
@Eugene, from iOS doc set, "For historical reasons, alloc
invokes allocWithZone:
.", so, there is no need to reimplement the alloc
method.