在阅读了对目标C中关于单身人士question的回复之后,似乎每个解决方案都在实例访问者中对线程进行了一些权衡。即。
@synchronized(self)
{
if (sharedInstance == nil)
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
这基本上是单线程访问单例,如果它是在操作中经常使用的东西,似乎可能会导致线程不必要地竞争。
简单地使用类对象作为单例实例并通过类方法公开功能有什么缺点,即
@interface MySingleton : NSObject {
}
+ (void)doSomething;
@end
@implementation MySingleton
+ (void)initialize {
//do some setup if necessary
}
+ (void)doSomething {
//do something
}
@end
这样我们每次想要引用单例对象时都避免进行锁定+检查,我们也可以省去将其存储在本地或方法ivar中。
此方法还允许运行时保证在任何给定时间系统中只存在一个实例(Class对象)。
修改
这里不仅仅是线程,使用传统的单例,您通常会编写如下代码:
MySingleton *instance = [MySingleton getSharedInstance];
NSObject *someResult = [instance getResult];
//or
if (instance.someProperty) {
//do something
}
但是,如果你的单例是一个类实例,你基本上不需要一直调用getSharedInstance。请考虑以下代码:
NSObject *someResult = [MySingleton getResult];
//or
if ([MySingleton someProperty]) {
//do something
}
我听说你必须将数据存储在文件本地静态变量或全局变量(yuck)中。但它确实与传统的单例不同,除了你丢失了Objective-C 2.0属性(而是你必须使用传统的访问器方法)。
这对我来说是一个关键的权衡,似乎是一场胜利。在一个传统的单例中你最终会覆盖-copyWithZone,+ allocWithZone,-retain,-retainCount,-release和-autorelease,如果你真的想要把事情做好。
每次想要编写一个简单的Singleton对象时,这似乎都是很多工作(它们恰好非常有用)。那么为什么不简单地用它替换它:
@implementation MySingleton
+ (void)initialize {
//do your setup
}
- (id)init {
NSAssert(NO, @"You should read the documentation on singletons.");
}
@end
代码方面要轻松得多,除非你的消费者真的偷偷摸摸,否则他们不会创建两个实例。
已经达到目的 我的问题是这样的:
使用Class对象作为单例的实例有什么缺点吗?
似乎你可以在线程安全性,内存效率等方面采取所有相同的步骤,而不必记住覆盖如此多的方法和访问器,或者使用实例检查来丢弃代码。
答案 0 :(得分:5)
对于iOS 4.0或更高版本,到目前为止最好的解决方案是使用dispatch_once
,如
+ (id)sharedInstance {
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
sharedInstance = [[MyClass alloc] init];
});
return sharedInstance;
}
您可能还想考虑使用单个dispatch_queue来序列化对类的内部的访问。如果所有公共方法只在同一个dispatch_queue上运行一个块,那么你就不必担心并发问题了。
答案 1 :(得分:3)
这是我关于Stack Overflow的第一篇文章......(所以准备愚蠢)
我认为有一种可能有用的混合解决方案。
我想在没有调用“getSharedInstance”的情况下从单例类中设置和获取(全局)值。我希望代码看起来像这样......
frameRate = Singleton.frameRate;
Singleton.frameRate = 42;
为了实现这一点,我们需要存储在单例中的每个变量都有一个getter和setter类方法。然后,类方法转到实例以将数据存储在ivar中。主程序不直接访问该实例。
getter看起来像这样:
+ (int) frameRate
{
return [[Singleton instance] ivarFrameRate];
}
(丑陋的)实例调用隐藏在类代码中。
通过在此处调用实例方法,类方法将在首次使用时自动实例化对象。一旦实例化单例,实例就会按常规存储ivars。在这里,我用“ivar”作为前缀,使ivar明确。
@property int ivarFrameRate;
和
@synthesize ivarFrameRate;
这会自动创建传统的getter(和setter)方法来访问ivar。
(编辑 - 这是一个完整的例子)
// Singleton.h
#import <Foundation/Foundation.h>
@interface Singleton : NSObject
{
float ivarFrameRate
}
@property float ivarFrameRate;
- (id) init;
+ (Singleton *) instance;
+ (float) frameRate;
+ (void) setFrameRate:(float)fr;
@end
和
// Singleton.m
#import "Singleton.h"
@implementation Singleton
@synthesize ivarFrameRate;
static Singleton* gInstance = NULL;
+ (Singleton*)instance
{
@synchronized(self)
{
if (gInstance == NULL)
gInstance = [[self alloc] init];
}
return(gInstance);
}
- (id)init
{
self = [super init];
return self;
}
+ (float) frameRate
{
return [[Singleton instance] ivarFrameRate];
}
+ (void) setFrameRate:(float)fr;
{
[[Singleton instance] setIvarFrameRate:fr];
}
答案 2 :(得分:0)
这很好,但仍然只是改变你的情况而不是修复你的问题。除非你没有任何与你的单身人士有关的实际数据,否则这将很好。无论何时访问中央数据,您都需要正确地使其成为线程安全的。
此外,如果没有某种iVar,我不知道如何直接在类中存储数据(这是预期的)。
在上面的示例中,我将以这种方式对其进行编码,获得与您提议的结果相同的结果,并且只有在我们创建/重新创建单例时才会获得性能:
if (sharedInstance)
return sharedInstance;
@synchronized(self)
{
if (sharedInstance == nil)
sharedInstance = [[MySingleton alloc] init];
}
return sharedInstance;
请记住,无论哪种方式,如果您正在访问可能在不同线程上发生变化的数据,那么无论如何您都必须使该代码成为线程安全的,无论是仔细规划还是使用代码来确保没有问题。我建议混合使用,但是当有疑问时,后者尽可能。 =)
答案 3 :(得分:0)
如果使用类作为单例,则存储数据的唯一方法是使用静态文件变量和全局变量。如果你要去做一个你不打算实例化的课程,你也可以只使用标准的C函数:
void doSomething(void);
void doSomething() {
//do something
}