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
Related
+ (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.
I have a singletone initialisation class method:
+ (instancetype)sharedInstance
{
static PanoramaDataManager *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[PanoramaDataManager alloc] init];
[sharedInstance getTokenForPanoramaAPIAccess];
});
return sharedInstance;
}
- (void) getTokenForPanoramaAPIAccess
{
NSDictionary *params = #{#"login" : kPanoramaAPILogin, #"password" : kPanoramaAPIPassword};
PanoramaDataCommand *command = [[PanoramaDataCommand alloc] initWithUrl:[self urlToService:kPanoramaAPIGetToken withParams:params]
data:nil
caption:#"Получение токена для доступа к API панорам"];
[command executeWithSuccess:^(PanoramaDataCommand *operation) {
dispatch_sync(dispatch_get_main_queue(), ^{
NSDictionary *result = [command result];
if (result)
self.token = result[#"token"];
NSLog(#"self.token = %#", self.token);
});
}
errorHandler:^(NSError *error) {
dispatch_sync(dispatch_get_main_queue(), ^{
[BxAlertView showError:[NSString stringWithFormat:#"Ошибка при получении токена: %#", error.description]];
});
}
cancelHandler:^{
}];
}
So I try to make a GET request to get token for the other requests, but now there is a problem - after the initialisation of the singleton some times later when all the rest requests are completed I only receive the message in log - that the token is received. I want to be sure that I get the token before return the shared instance. How could I do that? Is it a good approach? Thanks
You're using the wrong synchronization mechanism for the job. dispatch_once only makes sense for synchronous initialization. It's basically equivalent to this pseudocode (but much faster):
acquire global_mutex
if (!already_initialized)
initialize()
already_initialized = YES
release global_mutex
If that initialize() part kicks off an asynchronous job and then returns, the already_initialized flag gets set to YES—which is a lie, because you're not actually initialized until the asynchronous job finishes. So everyone else stomping around using uninitialized values.
The easiest way around this is to do the initialization before starting all of the other threads (or tasks in your thread pool) that need whatever you're initializing.
If that's not possible, then you need to move the already_initialized = YES equivalent to the end of the asynchronous initialization task. You can use a barrier, an atomic/interlocked/CAS integer, a condition variable, a lock around a boolean, whatever seems appropriate, but one thing you can't use is dispatch_once.
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;
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;
}
How can I make a viewcontroller singleton, to then use this code:
FacebookManager *manager = [FacebookManager sharedManager];
[manager openSessionWithAllowLoginUI:NO]
??
That's not necessarily a singleton. A singleton can only have one instance at any given time. Shared instances are similar, but don't prevent additional instances from being created.
You can implement a shared instance with a static variable and a class method like this:
+ (FacebookManager *)sharedManager
{
static FacebookManager *shaderManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
shaderManager = [[FacebookManager alloc] init];
});
return shaderManager;
}
Don't forget to declare the class method in your header.