我刚才读到,这个结构:
NSFileManager* fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDbtPath = [documentsDirectory stringByAppendingPathComponent:@"NimrokhRavanshenasi.sqlite3"];
BOOL success = [fileManager fileExistsAtPath:writableDbtPath];
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"NimrokhRavanshenasi.sqlite3"];
NSError *error;
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDbtPath error:&error];
NSFileManager *filemgr = [NSFileManager defaultManager];
sqlite3_stmt *compiledStatement;
if([filemgr fileExistsAtPath:writableDbtPath]){
if (sqlite3_open([writableDbtPath UTF8String], &_contactDB) != SQLITE_OK) {
NSLog(@"Failed to open database!");
是一种创建单例的线程安全且有效的方法吗?!我是否正确,每次调用此方法时,静态AppSettings变量都是相同的?!我对这个问题的范围有点困惑......
我的正常做法是使用unique_ptr作为我班级的静态成员......但这似乎有用......有人可以向我解释一下,这里发生了什么?!
顺便说一下:const在这里有意义吗?!
答案 0 :(得分:10)
在C ++ 11(和转发版)中,函数local static AppSettings
的构造保证是线程安全的。注意:在VS-2015之前,Visual Studio没有实现C ++ 11的这个方面。
编译器将在AppSettings
旁边放置一个隐藏标志,指示它是否为:
第一个线程将找到设置为“not construct”的标志并尝试构造该对象。成功构建后,标志将被设置为“已构建”。如果另一个线程出现并且发现标志设置为“正在构造”,它将等待该标志设置为“构造”。
如果构造因异常而失败,则该标志将被设置为“not construct”,并且将在下一次传递时重试构造(在同一个线程或不同的线程上)。
对象instance
将继续为您的计划的其余部分构建,直到main()
返回,此时instance
将被销毁。
每次执行任何线程都通过AppSettings::GetInstance()
时,它将引用完全相同的对象。
在C ++ 98/03中,不保证构造是线程安全的。
如果AppSettings
的构造函数以递归方式输入AppSettings::GetInstance()
,则行为未定义。
如果编译器可以在编译时看到如何构造instance
“,则允许它。
如果AppSettings
具有constexpr
构造函数(用于构造instance
的构造函数),并且instance
符合constexpr
,则编译器是必需的在编译时构造instance
。如果在编译时构造instance
,则“not-construct / construct”标志将被优化掉。
答案 1 :(得分:3)
您的代码行为类似于:
namespace {
std::atomic_flag initialized = ATOMIC_FLAG_INIT;
std::experimental::optional<bg::AppSettings> optional_instance;
}
const bg::AppSettings& bg::AppSettings::GetInstance()
{
if (!initialized.test_and_set()) {
optional_instance.emplace();
}
return *optional_instance;
}
通过在程序的整个持续时间内生成一个线程安全标志,编译器可以在每次调用函数时检查此标志,并仅初始化变量一次。一个真正的实现可以使用其他机制来获得同样的效果。