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

时间:2012-04-08 20:35:51

标签: iphone objective-c ios memory-management singleton

我在网上发现了单身人士模式。在我看来,它有许多可以优化的东西。

-In sharedMySingleton方法,无需调用retain?我不确定...
- 如果没有,为什么allocWithZone中有保留? - @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 个答案:

答案 0 :(得分:17)

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

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

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

答案 1 :(得分:0)

无需致电retain,因为有alloc。最重要的是调用retain会导致内存泄漏。 allocWithZone上有保留因为它是单例,所以我们不想创建该类的两个不同实例。而是分配一个新实例,我们增加了单例实例的保留计数。这是为什么?可能是为了防止有人不知道该类的单例类型。如果他调用allocWithZone然后释放实例,一切仍然正常,他实际上访问了共享的单例实例。

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

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

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

答案 2 :(得分:0)

  

在sharedMySingleton方法中,不需要调用retain?

alloc返回引用计数为1的新实例,因此不需要保留。

  

如果没有,为什么在allocWithZone中有保留?

根据规则,当您致电allocWithZone时,您拥有该引用,因此allocWithZone会大大增加您的引用次数。但是allocWithZone的这个实现正在返回已经由其他人创建并拥有的单例实例(sharedMySingleton方法)。因此sharedMySingleton方法使用alloc创建对象,因此它成为所有者。然后,您通过allocWithZone获得相同的实例,因此您成为同一实例的第二个所有者。因此,保留计数必须增加,因为现在有两个所有者。这就是allocWithZone需要保留的原因。

  

@synchronized有什么用?

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

  

NSAssert认为可以多次调用块,所以如果   是的,应该有更多的代码来释放以前的内存,或者   在没有NSAsserting的情况下清楚地退出块,如果不是,为什么是   有这个NSAssert?

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