I have created a singleton like this:
+ (instancetype)sharedDataStore {
static SSDataStore *_sharedDataStore = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedDataStore = [[self alloc] init];
});
return _sharedDataStore;
}
What is the best way of saving and loading it to and from the documents folder?
I tried saving it with NSKeyedArchiver, but I don't know how to load it afterwards. What do I do instead of
_sharedDataStore = [[self alloc] init];
?
To restore the state of your singleton, you need to implement the initWithCoder method from the NSCoding protocol. Something like:
- (id)initWithCoder:(NSCoder *)decoder {
self = [super init];
if (!self) {
return nil;
}
self.property1 = [decoder decodeObjectForKey:#"property1"];
self.property2 = [decoder decodeObjectForKey:#"property2"];
return self;
}
See NSCoding/NSKeyedArchiver on NSHipster for more details.
Related
Hy, I want to get the background location updates, so I wrote the below code and make it synchronized to keep it safe from multithreading. So I have two questions:
Do I really need to synchronize the piece of code, all I am doing in App is just running background task? I never created any special NSThread type to support multithreading and don't require as such?
Whenever I need to start the location updates, I call like this:
CLLocationManager *locationManager = [LocationTracker sharedLocationManager];
locationManager.delegate = self;
[locationManager startUpdatingLocation];
Is correct way to call?
+ (CLLocationManager *)sharedLocationManager {
static CLLocationManager *_locationManager;
#synchronized(self) {
if (_locationManager == nil) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[_locationManager requestAlwaysAuthorization];
_locationManager.distanceFilter = 10;
if(IS_OS_9_OR_LATER){
_locationManager.allowsBackgroundLocationUpdates = YES;
}
}
}
return _locationManager;
}
Shared instance should be synchronised. You can use #Synchronized or dispatch_once. It is good practice to synchronised the shared instances even you are not using the multiple threads.
+ (instancetype)sharedLocationManager {
static LocationTracker *sharedInstance_ = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance_ = [[LocationTracker alloc] init];
[sharedInstance_ initialize];
});
return sharedInstance_;
}
- (void)initialize {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
[_locationManager requestAlwaysAuthorization];
_locationManager.distanceFilter = 10;
if(IS_OS_9_OR_LATER){
_locationManager.allowsBackgroundLocationUpdates = YES;
}
}
You can implement the location manager delegates inside the LocationTracker class. Use NSNotificationObserver to update all the classes, which are all expecting location update.
Else follow the below line.
CLLocationManager *locationManager = [LocationTracker sharedLocationManager].locationManager;
locationManager.delegate = self;
[locationManager startUpdatingLocation];
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.
init method is declared in NSObject class hence, the client code can create a new instance of my singleton class, is there any way to achieve the real singleton such that client cannot create a new instance.
Just do this:
static SingletonClass *singleton;
+ (SingletonClass *)sharedInstance
{
#synchronized(self) { //For thread safety
if (singleton == nil) {
[[self alloc] init];
}
return singleton;
}
}
-(id)init
{
if (singleton) { //This way init will always return the same instance
return singleton;
}
self = [super init];
if (self) {
singleton = self;
}
return singleton;
}
This is a proper way to do a singleton in objective c
+ (id)sharedManager
{
static dispatch_once_t onceQueue;
static SingletonObjectClass *singleton = nil;
dispatch_once(&onceQueue, ^{
singleton = [[self alloc] init];
});
return singleton;
}
- (id)init {
self = [super init];
if (self) {
//.....
}
return self;
}
init method is for initialization of instance variables. on its own will not create the object. Alloc, copy methods needs to be overridden to achieve a real single ton.
Hope this should clarify.
+ (id)alloc {
NSLog(#"%#: use +sharedInstance instead of +alloc", [[self class] name]);
return nil;
}
+ (id)new {
return [self alloc];
}
+ (SingletonClass *)sharedInstance {
static SingletonClass *myInstance = nil;
if (!myInstance)
{
myInstance = [[super alloc] init];
}
return myInstance;
}
You can return a static object of the class each time, making it a singleton.
#implementation Singleton
#synthesize testVar;
+ (Singleton*) sharedObject {
static Singleton * myInstance = nil;
if (myInstance == nil) {
myInstance = [[[self class] alloc] init];
testVar = 5;
// Set default values if needed
return myInstance;
}
To access object and its members:
[[Singleton sharedObject] testVar]
I created a Singleton class named DataManager. I've set up some caching. However, when attempting to call the cache instance methods from within another instance method of DataManager, I'm getting EXC_BAD_ACCESS error on the line:
NSMutableArray *stops = [[DataManager sharedInstance] getCacheForKey:#"stops"];
DataManager.h
#interface DataManager : NSObject {
FMDatabase *db;
NSMutableDictionary *cache;
}
+ (DataManager *)sharedInstance;
// ... more methods
- (id)getCacheForKey:(NSString *)key;
- (void)setCacheForKey:(NSString *)key withValue:(id)value;
- (void)clearCache;
DataManager.m
- (NSArray *)getStops:(NSInteger)archived {
NSMutableArray *stops = [[DataManager sharedInstance] getCacheForKey:#"stops"];
if (stops != nil) {
NSLog(#"Stops: %#", stops);
return stops;
}
stops = [NSMutableArray array];
// set stops...
[[DataManager sharedInstance] setCacheForKey:#"stops" withValue:stops];
return stops;
}
It seems to occur when called from another view controller. That is the first view controller no error, second view controller, error.
This is my first attempt at a Singleton, so I'm sure I am making a simple mistake. But I am failing to see it myself.
Note: I've tried [self getCache...] with the same result.
UPDATE
Here is my Singleton implementation. Adapted from http://www.galloway.me.uk/tutorials/singleton-classes/
+ (DataManager *)sharedInstance {
#synchronized(self) {
if (!instance) {
instance = [[super allocWithZone:NULL] init];
}
}
return instance;
}
+ (id)allocWithZone:(NSZone *)zone {
return [[self sharedInstance] retain];
}
- (id)copyWithZone:(NSZone *)zone {
return self;
}
- (id)retain {
return self;
}
- (unsigned)retainCount {
return UINT_MAX;
}
- (oneway void)release {
// never release
}
- (id)autorelease {
return self;
}
- (id)init {
if (self = [super init]) {
if (db == nil){
BourbonAppDelegate *appDelegate = (BourbonAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate createEditableCopyOfDatabaseIfNeeded];
db = [[FMDatabase alloc] initWithPath:[appDelegate getDBPath]];
}
if (![db open]) {
NSAssert(0, #"Failed to open database.");
[db release];
return nil;
}
[db setTraceExecution:YES];
[db setLogsErrors:TRUE];
cache = [NSMutableDictionary dictionary];
NSLog(#"cache: %#", cache);
}
return self;
}
Your cache object is autoreleased, so it's no longer in memory when you try to access it.
Use [NSMutableDictionary alloc] init] instead of [NSMutableDictionary dictionary] to get an instance that is retained.
Not a direct answer to your question, which was already answered.
However I'd just like to point out that your singleton implementation is sub-optimal. #synchronized is very expensive, and you can avoid using it every time you access the singleton:
if (!instance) {
#synchronized(self) {
if (!instance) {
instance = [[super allocWithZone:NULL] init];
}
}
}
An even better way to initialize a singleton would be:
+ (DataManager *)sharedInstance {
static DataManager *instance;
static dispatch_once_t donce;
dispatch_once(&donce, ^{
instance = [[self alloc] init];
});
return instance;
}
How can i make sure user do not call init, instead client should call sharedSingleton to get a shared instance.
#synthesize delegate;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
+ (LoginController *)sharedSingleton
{
static LoginController *sharedSingleton;
#synchronized(self)
{
if (!sharedSingleton)
sharedSingleton = [[LoginController alloc] init];
CdtMiscRegisterConnectionChangeListenerObjc(test_ConnectionChangeListenerCallback);
return sharedSingleton;
}
}
I've seen it done two ways.
Throw an exception inside init.
Have the object returned by init be your singleton object.
Just to be clear, though, don't do this. It's unnecessary and will make your singletons overly difficult to test and subclass.
edit to add examples
Throw an exception in init
- (instancetype)init {
[self doesNotRecognizeSelector:_cmd];
return nil;
}
- (instancetype)initPrivate {
self = [super init];
if (self) {
}
return self;
}
+ (instancetype)sharedInstance {
static MySingleton *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] initPrivate];
});
return sharedInstance;
}
Have init return your singleton
- (instancetype)init {
return [[self class] sharedInstance];
}
- (instancetype)initPrivate {
self = [super init];
if (self) {
}
return self;
}
+ (instancetype)sharedInstance {
static MySingleton2 *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] initPrivate];
});
return sharedInstance;
}
Use UNAVAILABLE_ATTRIBUTE abolish init method, and implement initPrivate
+ (instancetype)shareInstance;
- (instancetype)init UNAVAILABLE_ATTRIBUTE;
+ (instancetype)new UNAVAILABLE_ATTRIBUTE;
implement
+ (instancetype)shareInstance {
static MyClass *shareInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shareInstance = [[super allocWithZone:NULL] initPrivate];
});
return shareInstance;
}
- (instancetype)initPrivate {
self = [super init];
if (self) {
}
return self;
}
// MARK: Rewrite
+ (id)allocWithZone:(struct _NSZone *)zone {
return [MyClass shareInstance];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
Short answer: you can't; Objective-C has no concept of private methods.
Check out the answer to this similar question.
You can't make methods private in Objective-C. You could raise a NSException if the wrong initializer is invoked.
- (id)init
{
[NSException exceptionWithName:#"InvalidOperation" reason:#"Cannot invoke init." userInfo:nil];
}