重置dispatch_once是否安全(不涉及线程)

时间:2014-05-05 16:21:47

标签: ios grand-central-dispatch

我想知道重置dispatch_once是否安全(不涉及线程):

我的情况是整个应用程序我缓存NSDateFormattersNSNumberFormatters等。我是通过将它们包裹在dispatch_once次调用中来实现的。

现在当我收到区域设置发生变化的NSNotification时,id就像重置了一些dispatch_once标记,所以下次需要格式化时,它们会再次分配。

我只是让每个vc观察通知并将标记的值重置为0.

这样可以吗?感觉......一方面有点脏,但也可以,因为替代方案是使用BOOLS并用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

2 个答案:

答案 0 :(得分:3)

The documentation of dispatch_once明确指出:"只执行一次"。

只要您想多次运行一个块,就不要使用dispatch_once。你也已经对它感到肮脏了。

相反,你应该运行这样的代码:

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;
}

附注:请注意NSDateFormatter不是线程安全的:如果在不同的线程中使用单个实例,则可能会遇到崩溃。如果可以的话,你最好坚持主线程。或者每个线程有不同的实例。但这超出了你的问题。

答案 1 :(得分:2)

dispatch_once单例模型用于提供线程安全性。如果你肯定只会从主UI线程访问单例(就像在这种情况下似乎那样),没有充分的理由不回到旧的单例模型:

static NSDateFormatter* myFormatter;

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

+(void)resetMyFormatter
{
    myFormatter = nil;
}

如果必须同时执行这两项操作(在多线程环境中重置),则可以包装格式化程序创建并在@synchronized(self)中重置。

我会避免修改dispatch_once_t的私有位,因为它没有记录它的使用方式(虽然它暗示将其重置为0会清除它,但它没有记录。)为了保持线程安全,无论如何你都必须将整个东西包裹在信号量中,所以不妨回到已知的文档化解决方案。