Domanda

Id like to know if it is safe to reset a dispatch_once (no threading involved):

my case is that all over the app I cache NSDateFormatters and NSNumberFormatters and other. I do this by wrapping them in dispatch_once calls.

Now when I get the NSNotification that the locale changed, id like to reset some of those dispatch_once tokens, so the next time the formatters are needed, they allocate again.

I would simply make each vc observe the notification and reset the values of the tokens to 0.

is this ok? It feels.. a bit dirty on the one hand but also ok since the alternative would be to use BOOLS and replace the dispatch call with an if..

#import "ViewController.h"

static dispatch_once_t onceToken;

@implementation ViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    [[NSNotificationCenter defaultCenter] addObserverForName:NSCurrentLocaleDidChangeNotification
                                                      object:self
                                                       queue:nil
                                                  usingBlock:^(NSNotification *note) {
                                                      onceToken = 0;
                                                  }
}

//image this called every second...
- (void)viewDidAppear:(BOOL)animated {
    //need a for matter only here
    static NSDateFormatter *formatter;
    dispatch_once(&onceToken, ^{
        formatter = [[NSDateFormatter alloc] init];
    });

    NSLog(@"%@", [formatter stringFromDate:[NSDate date]]);
}
@end
È stato utile?

Soluzione 2

The dispatch_once singleton model is used to provide thread-safety. If you're positive that the singleton will only ever be accessed from the main UI thread (as it seems in this case) there's no substantial reason not to fall back to the older singleton model:

static NSDateFormatter* myFormatter;

+(NSDateFormatter*)mySingletonFormatter
{
    if(!myFormatter)
    {
        myFormatter = [...];
    }
    return myFormatter;
}

+(void)resetMyFormatter
{
    myFormatter = nil;
}

If you have to do both (reset in a multithreaded environment) the you can wrap the formatter creation and reset in @synchronized(self).

I would avoid modifying the private bits of dispatch_once_t since it's undocumented how it's used (although it's implied that resetting it to 0 will clear it, it's not documented.) To maintain thread safety, you would have to wrap the whole thing in semaphores anyway, so might as well just fall back to a known documented solution.

Altri suggerimenti

The documentation of dispatch_once clearly states: "execute a block only once".

As soon as you want to run a block several times, don't use dispatch_once. And you already feel dirty about it also.

Instead, you should run code like this :

static NSDateFormatter *sharedDateFormatter;    // already initialized to nil

+ (NSDateFormatter *)sharedDateFormatter
{
    if (sharedDateFormatter == nil) {
        sharedDateFormatter = ...;
    }
    return sharedDateFormatter;
}

// should be called when relevant NSNotification occur.
+ (void)resetSharedDateFormatter
{
    sharedDateFormatter = nil;
}

Side note: beware that NSDateFormatter is not thread-safe: you may experience crashes if you use a single instance in different threads. You have better stick to the main thread, if you can. Or have a different instance per thread. But this goes beyond your question.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top