5

我在网上找到了这种单例模式。在我看来,它有很多可以优化的东西。

-在sharedMySingleton方法中,不需要调用retain 吗?我不确定... -
如果不是,为什么要保留 in 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
4

3 回答 3

17

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

我的 Objective-C 单例应该是什么样子?, 但其中大部分在 GCD 发布后就已经过时了。在 Mac 和 iOS 的现代版本中,您应该使用 Colin Barrett 在链接问题中给出的以下模式:

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

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

于 2012-04-08T20:52:26.333 回答
0

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

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

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

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

于 2012-04-08T21:00:33.067 回答
0

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

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

如果没有,为什么allocWithZone 中有retain?

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

@synchronized 有什么用?

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

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

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

于 2012-04-08T21:07:16.043 回答