Best singleton pattern for thread safe object [duplicate] - objective-c

This question already has answers here:
What should my Objective-C singleton look like? [closed]
(26 answers)
Closed 9 years ago.
Whats the best pattern for singleton? I frequently use
+ (SomeManager *)shared
{
static SomeManager * _SomeManager = nil;
if (_SomeManager) {
return _SomeManager;
}
_SomeManager = [[SomeManager alloc] init];
return _SomeManager;
}
Is this thread safe? If not, hot to make it safe?

Using an example from
Create singleton using GCD's dispatch_once in Objective C
+ (id)sharedInstance
{
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Clear and simple. Google better next time.

I prefer to use the GCD dispatch_once method...
+ (id) sharedSomethingManager
{
static dispatch_once_t onceQueue;
static SomethingkManager *somethingManager = nil;
dispatch_once(&onceQueue, ^{somethingManager = [[self alloc] init]; });
return somethingManager;
}

Related

Issue with two objects that create each other, infinite loop

I have two objects, DNLocations, that have arrays of other locations inside of them. The idea i'm trying to get across is that you can go from one location to another, and back to the previous one. Unfortunately, when i create the locations like this, they keep creating each other resulting in an infinite loop. I'm not sure what to do here. I've seen circular references but this seems like something else.
+ (DNLocation *) aCell {
static dispatch_once_t onceToken;
static DNLocation *instance = nil;
dispatch_once(&onceToken, ^{
instance = [DNLocation locationWithName:#"A Cell" andActions:[NSArray arrayWithObjects: [DNAction doNothingAction], nil] andLocations:[NSArray arrayWithObjects:[DNLocation aWhiteRoom], nil]];
});
return instance;
}
+ (DNLocation *) aWhiteRoom {
static dispatch_once_t onceToken;
static DNLocation *instance = nil;
dispatch_once(&onceToken, ^{
instance = [DNLocation locationWithName:#"A White Room" andActions:nil andLocations:[NSArray arrayWithObjects:[DNLocation aCell], nil]];
});
[instance setColor:[UIColor DNWhiteColor]];
[instance setFontColor:[UIColor DNBlackColor]];
return instance;
}
+ (DNLocation *) locationWithName:(NSString *) name {
return [[DNLocation alloc] initWithName:name];
}
These methods are called upon load of the app, setting up all of the possible locations to go to.
I'd rather not have to instantiate them and then add the locations to themselves, but if that is the only way then i'll do it.

Bad Access Code 1 when using a Singleton

I need some help.
I'm using this singleton pattern within an iOS application I'm developing:
.h file
#import <Foundation/Foundation.h>
#class Item;
#interface ItemManager : NSObject
- (id)init;
+ (ItemManager *)sharedInstance;
- (int)ratingFromObjectName:(NSString *)objectName;
#property(nonatomic,strong) NSArray *itemArray;
#end
.m file
static ItemManager *sharedInstance = nil;
+ (ItemManager *)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[ItemManager alloc] init];
});
return sharedInstance;
}
- (int)ratingFromObjectName:(NSString *)objectName {
for (int i = 0; i < itemArray.count; i++) {
if ([[[itemArray objectAtIndex:i] itemName] isEqualToString:objectName]) { //This is the line that throws bad access code 1
NSLog(#"Found: %# Returned: %d", [[itemArray objectAtIndex:i] ratingAverage],
[[[itemArray objectAtIndex:i] ratingAverage] intValue]);
return [[[itemArray objectAtIndex:i] ratingAverage] intValue];
}
}
return 0;
}
I get bad access when I use this in another class:
int rating = [[ItemManager sharedInstance] ratingFromObjectName:bla];
The bla object being sent is a NSString that is definitely working, it is 100% not the issue, as I have tested this. Removing the sharedInstance method and creating an array every time seems to work, however my attempt for this singleton is to avoid that, any suggestions would be greatly appreciated.
Please note that I have commented on the line returning the error.
Regards, WA
You need to work out which line of code is throwing the bad access. Is it the sharedInstance method or ratingFromObjectName:. I would first change the calling code to
ItemManager *manager = [ItemManager sharedInstance];
int rating = [manager ratingFromObjectName:bla];
As that will help with isolating the problem.
Secondly, I would also consider not using the Singleton pattern unless really necessary is iOS apps. It's been my experience that it is often overused (and in the Java world), and whilst convenient, can make writing unit tests more complicated.

Are there any cons to a semi singleton?

I just solved a really weird problem I had where I needed this class to behave sort of like a singleton but not really. Here's a code snippet
#implementation HMFPicturePreviewModalPanel
__weak static UIViewController *presentingInventoryViewController = nil;
static HMFPicturePreviewModalPanel *sharedPicturePreviewModalPanel = nil;
+(void)showPopupWithImage:(UIImage *)image withStartPoint:(CGPoint)startPoint withStartView:(UIView *)startView {
//this checks if there is already a panel visible.
if (![presentingInventoryViewController.view viewWithTag:kHMFPicturePreviewModalPanelTag]) {
sharedPicturePreviewModalPanel = [[HMFPicturePreviewModalPanel alloc] initWithFrame:presentingInventoryViewController.view.bounds withimage:image];
[presentingInventoryViewController.view addSubview:sharedPicturePreviewModalPanel];
[sharedPicturePreviewModalPanel showFromPoint:[startView convertPoint:startPoint toView:presentingInventoryViewController.view]];
}
}
+(void)changePresentingInventoryViewController:(UIViewController *)newInventoryViewController {
[sharedPicturePreviewModalPanel removeFromSuperView];
presentingInventoryViewController = newInventoryViewController;
}
+(void)removePresentingInventoryViewController {
[sharedPicturePreviewModalPanel removeFromSuperView];
presentingInventoryViewController = nil;
}
Is this called a semi-singleton? There's only ever going to be one of these on the screen at a time. I had to recreate this each time for it to work, hence why I couldn't do it as just a singleton.
What are the cons to this solution?
Also is it okay to have a __weak static variable?

Initializing NSImage(s) as static objects

I have a custom control in OSX with a drawing routine. Every instance of this control uses the same images.
I wonder if it's a good idea to instantiate these images as static objects available for all the instances created by the class.
In case that was a good idea, how can I implement the image loader in a cleaver way?
I thought to add to my class something like
static NSImage *imageone;
static NSImage *imagetwo;
static NSImage *imagethree;
But I'm not sure about the better way to initialize the images... maybe a singleton function like this?
+ (void)setupSharedImages {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
imageone = [NSimage imageNamed:#"...."];
imagetwo = [NSimage imageNamed:#"...."];
image three = [NSimage imageNamed:#"...."];
});
}
You can setup you images in + (void)initialize method. This method is called once when class is loaded and it also handles synchronization.
As an option, you may think of incapsulating images into "storage" singleton class.
#interface ImageStorage : NSObject
+ (ImageStorage *)sharedStorage;
- (NSImage *)imageForKey:(NSString *)key;
#end
#implementation ImageStorage {
NSDictionary *_images;
}
+ (ImageStorage *)sharedStorage
{
static dispatch_once_t onceToken;
static ImageStorage *sharedStorage;
dispatch_once(&onceToken, ^{
sharedStorage = [[ImageStorage alloc] init];
_images = [[NSDictionary dictionaryWithObjectsAndKeys:image1, key1, image2, key2, nil] retain];
});
return sharedStorage;
}
- (NSImage *)imageForKey:(NSString *)key
{
return [_images objectForKey:key];
}
#end
You could always lazy load them.
- (NSImage *)imageOne {
static NSImage *imageone;
if(imageone == nil)
imageone = [NSImage imageNamed:#"..."];
return imageone;
}
Edit:
Just wanted to clarify, do this for each image you need then in your draw method call
[self imageOne] etc...

iOS Singleton Variables Not Keeping Their Values

So I'm still kind of new to Objective-C, and this was my first app that I'm now updating. The idea is this: The whole app is basically various lists of stuff. It asks the API for 15 posts, shows those with a Load More button. Click Load More, it loads 15 more, etc. The API that it loads these from has a token system with a timeout built in. Too long between requests, and you have to get a new token. So I want to have a singleton to use anywhere in my app so I can just do [APIMachine getToken] and behind the scenes, it checks if the time since the last request was too long (or this is the first request), if so, gets a new token, otherwise returns the one we already have. I'm following the singleton pattern I've found in so many places, but every time the Load More button uses [APIMachine getToken]it gets either nothing or something completely random. I had it print this stuff in the logs, and one time I even got a UITableViewCell as my token. Looks like variables are being overwritten somehow. But I really can't figure it out.
So here it is:
static PoorAPI2 *_instance;
#implementation PoorAPI2
#synthesize apiToken, timeOpened, tokenTTL;
+ (PoorAPI2*)sharedAPI
{
#synchronized(self) {
if (_instance == nil) {
_instance = [[super allocWithZone:NULL] init];
}
}
return _instance;
}
-(NSString *)API_open{
//boring code to get api token redacted
if ([doneness isEqualToString:#"success"]) {
NSDictionary *data = [json objectForKey:#"data"];
apiToken = [data objectForKey:#"api_token"];
tokenTTL = [data objectForKey:#"ttl"];
timeOpened = [NSDate date];
}else{
NSLog(#"FFFFFFFUUUUUUUUUUUU this error should be handled better.");
}
return apiToken;
}
-(BOOL)isConnectionOpen{
return ([timeOpened timeIntervalSinceNow] > tokenTTL);
}
-(NSString *)getToken{
if([self isConnectionOpen]){
return apiToken;
}else{
return [_instance API_open];
}
}
-(id)init{
if(self = [super init]){
apiToken = [[NSString alloc] initWithString:#""];
timeOpened = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
tokenTTL = 0;
}
return self;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [[self sharedAPI]retain];
}
- (id)copyWithZone:(NSZone *)zone
{
return self;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
return NSUIntegerMax; //denotes an object that cannot be released
}
- (void)release
{
//do nothing
}
- (id)autorelease
{
return self;
}
#end
I can only hope I'm doing something seriously foolish and this will be a hilarious point-and-laugh-at-that-guy thread. Then at least my app will work.
In API_open, you store three objects in instance variables, but they're not objects you own, so they'll probably be gone by the time you need them and replaced by something unpredictable. You need to retain them or use proper setters.
You problem is:
static PoorAPI2 *_instance;
C, and by inheritance Objective-C, do not initialize variables. Just change to:
static PoorAPI2 *_instance = nil;
Also I am of the school that adding extra code to try to prevent the singleton from being used as a single is a total waste of time, and only give you more code with more possibilities for bugs.
So if I was you then I would remove every method from +[PoorApi2 allocWithZone:] and down. Objective-C is a dynamic language and if a client wanted to instantiate a second instance of your singleton then it would be able to do so despite all your wasted extra lines of code. At the most I would add a log like this:
-(id)init{
if (_instance) NSLog(#"WARNING: PoorAPI2 already has a shared instance.");
if(self = [super init]){
apiToken = [[NSString alloc] initWithString:#""];
timeOpened = [[NSDate alloc] initWithTimeIntervalSinceNow:0];
tokenTTL = 0;
}
return self;
}
Creating a second instance of a singleton is a programming error and should be caught in development. Not a problem you should add extra lines of code to hide.