Getting access to a sharedInstance from within a Class method - ios

I am converting a project to an SDK. I need to convert several instance methods to class methods. I am getting a compiler warning about using "self". The warning is "Incompatible pointer types initializing Store* with an expression of Class. This Store class is a singleton sharedInstance.
I have method like this in my class Store:
+ (void) dispatchStoreSource {
__weak Store *ref = self; <--- issue is here
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSArray *things = [ref fetchThings:&error];
//dispatch back to main queue
if (![ref updateSource:source forUser:user error:&error]) {
dispatch_async(dispatch_get_main_queue(), ^{
result(nil, error);
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
result(source, nil);
});
}
});
}
What is the proper way to fix this? Should it I do this?
__weak Store *ref = [Store sharedInstance];

Your ref is pointer to an object of Store class. But self in your class method doesn't point to an allocated object of your class (= your singleton), it's your Class, IOW Store (not object, but Class). If you have implemented sharedInstance class method, like this ...
+ (instancetype)sharedInstance {
static Story *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
... just do this ref = [self sharedInstance];

Yes, You should go with __weak Store *ref = [Store sharedInstance];
Otherwise Let's use your original static reference of Store.
Example :
static Store = _store = nil;
__weak Store * ref = _store;

Related

Get specific instance of class from inside super class class method

+ (NSURLSessionDataTask *)login:(NSString*)email andPassword:(NSString*)password andCallback:(void (^)(NSArray *responseArray, NSError *error))block {
if(![self hasInternet]){return nil;}
NSLog(#"Session.login");
[APIClient sharedClient].requestSerializer = [AFJSONRequestSerializer serializer];
[[APIClient sharedClient].requestSerializer setValue:email forHTTPHeaderField:#"email"];
[[APIClient sharedClient].requestSerializer setValue:password forHTTPHeaderField:#"password"];
[[APIClient sharedClient].requestSerializer setValue:#"poop" forHTTPHeaderField:#"apikey"];
return [[APIClient sharedClient] POST:#"/login" parameters:nil progress:nil success:^(NSURLSessionDataTask * __unused task, id JSON) {
NSLog(#"session.loginWithEmail.response:%#",JSON);
if([JSON objectForKey:#"user"]){
NSMutableDictionary *user=[[NSMutableDictionary alloc] initWithDictionary:[[JSON objectForKey:#"user"] copy]];
[user setObject:password forKey:#"password"];
[[Session sharedInstance] startSession:user];
if([[Session sharedInstance] isSessionActive]){
if([JSON objectForKey:#"req_onboarding"]){
NSLog(#"session.onboard!=nil");
[Session sharedInstance].requiredOnboarding=[JSON objectForKey:#"req_onboarding"];
}
if (block) {
NSLog(#"session.login.block.success");
block(nil, nil);
}
}else{
NSLog(#"Failed to set session");
}
}
} failure:^(NSURLSessionDataTask *__unused task, NSError *error) {
if (block) {
NSLog(#"Session.login.Fail");
block([NSArray array], error);
}
}];
}
I needed a sub-class-able singleton in order to be able to have a abstracted session manager that does most of the lifting,but can still be subclassed so that multiple sessions can co-exist and still have the power of being available throughout my app. Im building somewhat of a demo of all my apps which is why this functionality is important.
All was going well until I realized that my api methods that are hosted in the super session class were referencing the singleton itself to set the session, this is a problem bc sharedInstance is referenced like so:
+ (instancetype)sharedInstance
{
NSLog(#"[Master sharedInstance]");
id sharedInstance = nil;
#synchronized(self) {
NSLog(#"MS | synchronized(self)");
NSString *instanceClass = NSStringFromClass(self);
// Looking for existing instance
sharedInstance = [_sharedInstances objectForKey:instanceClass];
// If there's no instance – create one and add it to the dictionary
if (sharedInstance == nil) {
NSLog(#"MS | sharedInstance == nil");
sharedInstance = [[super allocWithZone:nil] init];
[_sharedInstances setObject:sharedInstance forKey:instanceClass];
NSLog(#"MS | SharedInstances:%#",_sharedInstances);
}
}
return sharedInstance;
}
When it was just the one Session singleton I could get away with doing this in class methods: [Session sharedInstance] isSessionActive]
but now, its essential that [______ sharedInstance] isSessionActive];
is a reference to the specific subclass calling the class method. Is it possible to retrieve reference the specific instance from within this class method shy of sending it as a param?
It looks like the aim is to distribute a singleton per subclass of the super (probably abstract) class. The sharedInstance code doesn't quite do that because this line:
sharedInstance = [[super allocWithZone:nil] init];
will create instances only of the superclass. I think you want instances of the subclasses so that you get access to the overridden data and behavior.
If I understand your aim correctly, then the fix is simple:
sharedInstance = [[self allocWithZone:nil] init]; // notice "self"
With this, when you send sharedInstance to ClassA, you'll get a (single) instance of ClassA. When you send it to ClassB, you'll get a (single) instance of ClassB. With change I suggest, those will really be instances of the subclasses A and B, not an instance of the class they both inherit from. If they've each overridden isSessionActive or any other superclass method, the caller will get the distinct, overridden implementation based on which class singleton they ask for.

What is the proper way of making weak reference of Singleton class

I came across a situation where I am using sharedClass and in it I fetched data using blocks. There is a convention to use __weak reference inside blocks to prevent retain cycle.How do I create a weak reference of something which is going to remain alive throughout the app?
Here is the code what I did,
+ (instancetype)sharedPresenter {
static PostCareListPresenter *presenterInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
presenterInstance = [[PostCareListPresenter alloc] init];
});
return presenterInstance;
}
- (void)getPostCareList:(NSDictionary*)pParameterDictionary
success:(void(^)(NSData*responseData))pSuccessCallback
failure:(void(^)(NSError* error))pFailureCallback {
[[GSWebAccessManager sharedManager] invokePostCareList:pParameterDictionary success:^(NSData *responseData) {
// Here I have to make a call to my private method using weak self. [weakSelf somePrvateMethod];
} failure:^(NSError *error) {
}];
}
As a singletone object is never to be destroyed you should not care if it is retained or not

Singleton + initialization code

I have a singleton that is initialized like all singletons, with something like this:
+ (MySingleton *)sharedInstance
{
static MySingleton *sharedMyInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyInstance = [[MySingleton alloc] init];
});
return sharedMyInstance;
}
In my case I want to add some code to initialize the singleton but because sharedInstance is a class method I cannot call instance methods from there.
So I always have to have this pattern:
MySingleton *sing = [MySingleton sharedInstance];
[sing initialize];
Ok, I can do this
MySingleton *sing = [[MySingleton sharedInstance] initialize];
but this will generate another problem because if initializing the singleton is the only thing I want at this point, sing is not being used and this is ugly code.
I suppose I can do simply
[[MySingleton sharedInstance] initialize];
and Xcode will not complain, but this does not sound good.
Is there another way to do this?
Check your code ;-) Specifically the line:
sharedMyInstance = [[MySingleton alloc] init];
You have to implement init and there is where you'll initialize the instance variables of your singleton (AKA shared instance). It will be called the first time the shared instance is used.

Possible to set singleton back to nil?

I have implemented a singleton object using the regular pattern. My question is: is it possible to set this object back to nil, so that on a later called to [MySingleton sharedInstance] the object gets re-initialised?
// Get the shared instance and create it if necessary.
+ (MySingleton *)sharedInstance {
static dispatch_once_t pred;
static MySingleton *shared = nil;
dispatch_once(&pred, ^{
shared = [[MySingleton alloc] init];
});
return shared;
}
// We can still have a regular init method, that will get called the first time the Singleton is used.
- (id)init
{
self = [super init];
if (self) {
// Work your initialising magic here as you normally would
}
return self;
}
My guess is that
MySingleton *shared = [MySingleton sharedInstance];
shared = nil;
only sets the local pointer shared to nil. After all, shared is declared as static.
Your assumption about the local reference is correct, it won't affect your singleton.
To be able to reinitialize the singleton you need to move the static variable out of your method, so it's accessible by the whole class.
static MySingleton *sharedInstance = nil;
// Get the shared instance and create it if necessary.
+ (MySingleton *)sharedInstance {
if (sharedInstance == nil) {
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
}
+ (void)resetSharedInstance {
sharedInstance = nil;
}
Note that you cannot use dispatch_once anymore, since your singleton needs obviously to be created multiple times. If you only ever call this singleton from your UI (and therefore only from the main thread), then the sample above is fine.
If you need access from multiple threads you need to put a lock around the +sharedInstance and +resetSharedInstance method, e.g.
+ (id)sharedInstance {
#synchronized(self) {
if (sharedInstance == nil) {
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
}
}
+ (void)resetSharedInstance {
#synchronized(self) {
sharedInstance = nil;
}
}
This is quite a bit slower than the dispatch_once variant, but in practice it won't matter usually.
Yeah, but your singleton's sharedInstance method defines it as a static inside that method, and your final code sample is just setting a local variable (coincidentally also called shared) to nil, leaving the static inside sharedInstance unaltered. Thus you are just nil-ing a local pointer, not changing the static inside sharedInstance.
If you want to do what you're asking, you'll have to pull the static variable, shared, out of the sharedInstance method (and presumably write some reset method to nil it). Your sharedInstance method also can no longer rely upon dispatch_once, but rather have to check to see if that static is nil or not.
I did this. I'm not sure if it's the best way but it seemed to work fine.
static dispatch_once_t pred;
static MySingleton *shared = nil;
+(MySingleton *)sharedInstance {
dispatch_once(&pred, ^{
shared = [[MySingleton alloc] init];
});
return shared;
}
+(void)clearSharedInstance {
shared = nil;
pred = nil;
}

class tracking and limiting instances with an NSSet

I'd like my class to detect that a new instance is equivalent (vis a vis isEqual: and hash) to some existing instance, and create only unique instances. Here's code that I think does the job, but I'm concerned it's doing something dumb that I can't spot...
Say it's an NSURLRequest subclass like this:
// MyClass.h
#interface MyClass : NSMutableURLRequest
#end
// MyClass.m
#implementation MyClass
+ (NSMutableSet *)instances {
static NSMutableSet *_instances;
static dispatch_once_t once;
dispatch_once(&once, ^{ _instances = [[NSMutableSet alloc] init];});
return _instances;
}
- (id)initWithURL:(NSURL *)URL {
self = [super initWithURL:URL];
if (self) {
if ([self.class.instances containsObject:self])
self = [self.class.instances member:self];
else
[self.class.instances addObject:self];
}
return self;
}
// Caller.m
NSURL *urlA = [NSURL urlWithString:#"http://www.yahoo.com"];
MyClass *instance0 = [[MyClass alloc] initWithURL: urlA];
MyClass *instance1 = [[MyClass alloc] initWithURL: urlA]; // 2
BOOL works = instance0 == instance1; // works => YES, but at what hidden cost?
Questions:
That second assignment to self in init looks weird, but not insane.
Or is it?
Is it just wishful coding to think that second alloc (of instance1) gets magically cleaned up?
It's not insane, but in manual retain/release mode, you do need to release self beforehand or you'll leak an uninitialized object every time this method is run. In ARC, the original instance will automatically be released for you.
See #1.
BTW, for any readers who usually stop at one answer, bbum's answer below includes a full working example of a thread-safe implementation. Highly recommended for anyone making a class that does this.
Thought of a better way (original answer below the line) assuming you really want to unique by URL. If not, this also demonstrates the synchronization primitive use.
#interface UniqueByURLInstances:NSObject
#property(strong) NSURL *url;
#end
#implementation UniqueByURLInstances
static NSMutableDictionary *InstanceCache()
{
static NSMutableDictionary *cache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [NSMutableDictionary new];
});
return cache;
}
static dispatch_queue_t InstanceSerializationQueue()
{
static dispatch_queue_t queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
queue = dispatch_queue_create("UniqueByURLInstances queue", DISPATCH_QUEUE_SERIAL);
});
return queue;
}
+ (instancetype)instanceWithURL:(NSURL*)URL
{
__block UniqueByURLInstances *returnValue = nil;
dispatch_sync(InstanceSerializationQueue(), ^{
returnValue = [InstanceCache() objectForKey:URL];
if (!returnValue)
{
returnValue = [[self alloc] initWithURL:URL];
}
});
return returnValue;
}
- (id)initWithURL:(NSURL *)URL
{
__block UniqueByURLInstances* returnValue = self;
dispatch_sync(InstanceSerializationQueue(), ^{
returnValue = [InstanceCache() objectForKey:URL];
if (returnValue) return;
returnValue = [super initWithURL:URL];
if (returnValue) {
[InstanceCache() setObject:returnValue forKey:URL];
}
_url = URL;
});
return returnValue;
}
- (void)dealloc {
dispatch_sync(InstanceSerializationQueue(), ^{
[InstanceCache() removeObjectForKey:_url];
});
// rest o' dealloc dance here
}
#end
Caveat: Above was typed into SO -- never been run. I may have screwed something up. It assumes ARC is enabled. Yes, it'll end up looking up URL twice when using the factory method, but that extra lookup should be lost in the noise of allocation and initialization. Doing that means that the developer could use either the factory or the initializer and still see unique'd instances but there will be no allocation on execution of the factory method when the instance for that URL already exists.
(If you can't unique by URL, then go back to your NSMutableSet and skip the factory method entirely.)
What Chuck said, but some additional notes:
Restructure your code like this:
+(NSMutableSet*)instances
{
static NSMutableSet *_instances;
dispatch_once( ...., ^{ _instances = [[NSMutableSet alloc] init];});
return instances;
}
Then call that method whenever you want access to instances. It localizes all the code in one spot and isolates it from +initialize (which isn't really a big deal).
If your class may be instantiated from multiple threads, you'll want to surround the check-allocate-or-return with a synchronization primitive. I would suggest a dispatch_queue.

Resources