【问题标题】:How do I ensure there is only one instance of a class at any one time in Objective-C?如何确保在 Objective-C 中任何时候只有一个类的实例?
【发布时间】:2014-12-28 07:07:01
【问题描述】:

这与标准的singleton pattern 略有不同,如果一个对象的所有外部引用都已释放,那么单例也将被释放。然后,稍后,当请求一个新对象时,会创建一个新的单例。所以,是这样的:

MyThing *thing1 = [MyThing new];
MyThing *thing2 = [MyThing new];
// thing1 & thing2 are the same object.

thing1 = nil;
thing2 = nil;

thing1 = [MyThing new];
thing2 = [MyThing new];
// thing1 and thing2 are a the same objet, but it's a different object than above.

我尝试使用一个弱静态变量来保留我的作用域单例,但这不起作用,因为我无法在 ARC 下增加保留计数。这让我想知道:这可能吗?

【问题讨论】:

  • 你为什么需要这个?
  • 似乎通过工厂访问的弱引用可以做到这一点。工厂必须像单身工厂一样工作。
  • 因为我不会经常需要这个对象,但是当我需要的时候,我会想同时在一堆解耦的地方访问同一个对象。
  • thing1 和 thing 2 不是同一个对象。它们是 MyThing 类的两个不同实例。
  • @HotLicks 这就是我的想法,但它实际上并没有工作。也许我不应该在-dealloc 中将nil 分配给它?如果它很弱,当最终引用被释放时,它是否会自行转到nil

标签: objective-c singleton retaincount


【解决方案1】:

覆盖 allocWithZone: 管理单个静态实例。如果为 nil,则创建一个新版本,如果不是 nil,则返回它的保留版本。

实现 dealloc 并在调用时为 nil 单个静态实例。

我不确定这是否适用于 ARC,您可能需要为该文件禁用 arc。

保留对象的成本是多少?遵循标准的单例模式并忘记它当然不会那么麻烦。

【讨论】:

  • 你还需要调配init,这样如果你得到了已经存在的对象,它就不会重新初始化。
  • 是的,这似乎是正确的答案。我在-init 中执行此操作,但将其移至+allocWithZone:,现在它按预期工作。现在只需要弄清楚如何让-init 对现有对象不做任何事情......
  • 我想我明白了,虽然它不是通用的(检查是否在-init 中设置了 ivar)。测试通过,但在释放对象之后,在创建新对象之前,我必须调用NSLog() 或者我会返回一个具有相同地址的对象。即使使用NSLog(),我有时仍然会在同一地址获得一个对象。
【解决方案2】:

根据@KirkSpaziani 的回答,这是我想出的:

static __weak MyTHing *currentThing = nil;

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    __block id thing = nil;
    dispatch_sync(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        if (currentThing == nil) {
            currentThing = thing = [super allocWithZone:zone];
        } else {
            thing = currentThing;
        }
    });
    return thing;
}

- (void)dealloc {
    dispatch_sync(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        currentThing = nil;
    });
}

这假设初始化程序可以处理“脏数据”,如described by Mike Ash

【讨论】:

  • 现在,如果我能阻止 Objective-C 在对象被释放后使用相同的内存地址,我就可以进行一个很好的测试来确保它正常工作。实际上,我可以通过添加NSLog() 调用日志来查看它的工作原理,但我会更喜欢可靠的单元测试。
  • 登录dealloc?这应该是最可靠的方法。此外,如果多个线程可以创建这些对象,请考虑使用 GCD 的 dispatch_sync 而不是 @synchronized(self) - 它应该更高效。
  • @KirkSpaziani 好电话,谢谢。是的,我通过在-dealloc 中进行日志调用确认它有效,但我需要的是在单元测试运行时分配不使用相同的内存地址。
猜你喜欢
  • 1970-01-01
  • 2022-01-05
  • 2013-12-08
  • 2020-09-21
  • 1970-01-01
  • 1970-01-01
  • 2013-03-01
  • 1970-01-01
  • 2011-11-25
相关资源
最近更新 更多