Objective-C - 优化这个单例模式?

5

我在网上找到了单例模式。但是,我认为它有很多可以优化的地方。

- 在 sharedMySingleton 方法中,是否不需要调用 retain?我不确定...
- 如果不需要,为什么在 allocWithZone 中有一个retain?
- @synchronized 的作用是什么?NSAssert 让人们认为该块可能会被多次调用,因此如果是这样,应该有一些更多的代码来释放以前的内存,或者清楚地退出块而不仅仅是NSAsserting。如果不是这样,为什么会有这个 NSAssert?
- sharedMySingletonalloc 之间的链似乎很奇怪。我自己写的东西就像:

+(MySingleton*)sharedMySingleton
{
    @synchronized([MySingleton class])
    {
        if (_sharedMySingleton == nil) _sharedMySingleton = [[self alloc] init];
        return _sharedMySingleton;
    }

    return nil;
}

+(id)alloc
{
    @synchronized([MySingleton class])
    {
        return [super alloc];
    }

    return nil;
} 

单例模式

#import "MySingleton.h"

@implementation MySingleton

// ##########################################################################################################
// ######################################## SINGLETON PART ##################################################
// ##########################################################################################################
static MySingleton* _sharedMySingleton = nil;

// =================================================================================================
+(MySingleton*)sharedMySingleton
// =================================================================================================
{
    @synchronized([MySingleton class])
    {
        if (_sharedMySingleton == nil) [[self alloc] init];
        return _sharedMySingleton;
    }

    return nil;
}

// =================================================================================================
+(id)alloc
// =================================================================================================
{
    @synchronized([MySingleton class])
    {
        NSAssert(_sharedMySingleton == nil, @"Attempted to allocate a second instance of a singleton.");
        _sharedMySingleton = [super alloc];
        return _sharedMySingleton;
    }

    return nil;
} 

+ (id)allocWithZone:(NSZone *)zone  { return [[self sharedMySingleton] retain]; }
- (id)copyWithZone:(NSZone *)zone   { return self; }
- (id)retain                        { return self; }
- (NSUInteger)retainCount           { return NSUIntegerMax;  /* denotes an object that cannot be released */}
- (oneway void)release              { /* do nothing */ }
- (id)autorelease                   { return self; }

// ##########################################################################################################
// ##########################################################################################################
// ##########################################################################################################

// =================================================================================================
-(id)init 
// =================================================================================================
{   
    if (!(self = [super init])) return nil;

    return self;
}

// =================================================================================================
-(void) dealloc
// =================================================================================================
{
    [super dealloc];
}

// =================================================================================================
-(void)test 
// =================================================================================================
{
    NSLog(@"Hello World!");
}

@end
3个回答

17

你应该根本不使用这种模式(它是用于一种非常特殊的单例情况,几乎从不需要使用,即使在那种情况下,你通常也不应该使用它)。

What should my Objective-C singleton look like?中讨论了很多好的模式,但大部分都已过时,因为GCD的发布。在现代的Mac和iOS版本中,应该使用由Colin Barrett在链接问题中提供的以下模式:

+ (MyFoo *)sharedFoo
{
    static dispatch_once_t once;
    static MyFoo *sharedFoo;
    dispatch_once(&once, ^{ sharedFoo = [[self alloc] init]; });
    return sharedFoo;
}

我只是将它复制到这里,而不是将问题标记为重复,因为旧问题的最高评级答案已经过时了。


其他的方法呢:保留(retain)、保留计数(retaincount)、释放(release)、copyWithZone等等?你为什么要谈论“现代”版本?你所说的“现代”是什么意思?你有没有想过为什么苹果没有使用这种代码更新其单例片段? - Oliver
1
现代指的是自GCD引入以来。您不应该覆盖这些方法。唯一展示覆盖它们的示例代码在此处:https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/CocoaObjects.html 正如它所指出的,这是单例的严格实现,上面的文本解释了通常不需要这样做。我已经打开了一个文档案例来改进这个文档,因为它让很多开发人员感到困惑。Mike Ash在这方面有一个很好的帖子:http://www.mikeash.com/pyblog/friday-qa-2009-10-02-care-and-feeding-of-singletons.html - Rob Napier
在10.6和iOS中是一样的。 - Rob Napier
dispatch_once() 函数在 iOS 4.0 中被添加。 - Rob Napier
1
你可以在这里返回MyFoo。很多片段都有'id',所以剪切和粘贴会更容易一些,但是MyFoo更好。编辑后更清晰明了。 - Rob Napier
显示剩余3条评论

0

不需要调用retain,因为有一个alloc。在顶部调用retain会导致内存泄漏。

allocWithZone上有保留,因为它是单例,所以我们不想创建两个不同的类实例。而是分配新实例,我们增加了单例实例的保留计数。为什么这样做?可能是为了防止某人不知道类的单例类型。如果他调用allocWithZone,然后释放该实例,一切都还好,他实际上访问了共享的单例实例。

@synchronized用于防止两个不同线程的两个调用同时进入if语句。因此代码是线程安全的。

NSSAssert可能是为了使应用程序在创建两个实例的单例时“崩溃”。这是“只是为了确保”的代码,也称为防御性编程。

关于sharedMySingletonalloc之间的链,我认为很好。


0
在 sharedMySingleton 方法中,不需要调用 retain 吗?
alloc 返回一个引用计数为 1 的新实例,因此不需要 retain。
如果不需要,为什么在 allocWithZone 中有一个 retain?
根据规则,当您调用 allocWithZone 时,您拥有该引用,因此 allocWithZone 必须为您增加引用计数。但是,这个 allocWithZone 的实现返回了已经由其他人(sharedMySingleton 方法)创建和拥有的单例实例。因此,sharedMySingleton 方法使用 alloc 创建对象,因此它成为所有者。然后,您通过 allocWithZone 获取相同的实例,因此您成为相同实例的第二个所有者。因此,保留计数必须增加,因为现在有两个所有者。这就是为什么 allocWithZone 需要保留的原因。
@synchronized 的用途是什么?

@synchronized 允许多个线程同时调用代码。如果您永远不会从多个线程调用 sharedMySingleton,那么它是不必要的,您可以省略它。

NSAssert 让人们认为该块可能会被多次调用,因此如果是这样,应该有一些更多的代码来释放先前的内存,或者清晰地退出该块而不仅仅是 NSAsserting,如果不是,为什么会有这个 NSAssert?

由于该类旨在成为单例,因此只应调用一次 alloc。如果多次调用 alloc,则 NSAssert() 终止程序。由于 NSAssert() 终止程序,因此当第二次调用 alloc 时,不需要进行内存管理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接