【问题标题】:Singleton Implementation. Blocking the alloc and init methods for external usage单例实现。阻止外部使用的 alloc 和 init 方法
【发布时间】:2014-01-01 09:19:10
【问题描述】:

我有一个希望实现为单例的类。

我希望创建/访问此类实例的唯一方法是通过:

+(MYClass*)sharedInstance 

方法。 alloc 和 init 在方法实现中被调用(当然)。

如果尝试不通过 sharedInstance 方法(而是直接通过 alloc + init)创建该类的实例,有没有办法阻止 alloc 和 init 的使用,或者使它们“空”?

【问题讨论】:

    标签: ios singleton init alloc singleton-methods


    【解决方案1】:

    如果在实现之外调用init,则将此代码放在头文件中会产生编译时错误:

    - (id)init __attribute__((unavailable("cannot use init for this class, use +(MYClass*)sharedInstance instead")));
    

    我发现了这种技术here

    更新:

    您将无法在 .m 文件中写入 [[MYClass alloc] init],但您可以使用以下内容:

    + (MYClass *)sharedInstance {
        static MYClass *sharedInstance;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sharedInstance = [self new];
        });
        return sharedInstance;
    }
    

    【讨论】:

    • 等一下......如果我这样做,我会阻止在 sharedInstance 方法中分配和初始化单例实例的能力......有没有办法只在外部定义这个语句类或向异常添加异常,以便我仍然可以在 .m 文件中使用它们?
    • 这行得通...有点矫枉过正,但确实有效。你很好,伙计......但是如果我也尝试对“alloc”做同样的事情(使用不可用的东西),我会收到一个错误。知道在那里做什么吗?
    • 如果您在 .h:+ (id)alloc __attribute__((unavailable("no alloc"))); 中使用类似的东西停止分配,那么您可以将我的答案中的 sharedInstance 行更改为sharedInstance = [self new],我会更新我的答案像你问的那样停止使用 alloc 和 init。
    【解决方案2】:

    如果您使用的是 arc,请将编译器标志 -fno-objc-arc 添加到文件中。

    “alloc + init”会调用sharedManager方法。

    static MyGizmoClass *sharedGizmoManager = nil;
    
    + (MyGizmoClass*)sharedManager
    {
        if (sharedGizmoManager == nil) {
            sharedGizmoManager = [[super allocWithZone:NULL] init];
        }
        return sharedGizmoManager;
    }
    
    + (id)allocWithZone:(NSZone *)zone
    {
        return [[self sharedManager] retain];
    }
    
    - (id)copyWithZone:(NSZone *)zone
    {
        return self;
    }
    
    - (id)retain
    {
        return self;
    }
    
    - (NSUInteger)retainCount
    {
        return NSUIntegerMax;  //denotes an object that cannot be released
    }
    
    - (void)release
    {
        //do nothing
    }
    
    - (id)autorelease
    {
        return self;
    }
    

    【讨论】:

    • 请注意,如果您使用 ARC,这将无法编译。
    • 如果你有一个-init 方法,这很可能会导致奇怪的行为,因为[[MyGizmoClass alloc] init] 会再次调用你已经实例化的单例的初始化程序。最好在 +allocWithZone: 中抛出异常,而不是让程序员知道他应该使用单例访问器。
    • @Fönsi 我同意你的观点,但是当-init 方法没有做任何重要的事情时也可以。
    • @Fönsi 有一个解决方案是在init中使用dispatch_once来确保init方法中的实例只初始化一次。
    • @KudoCC 是的,您可以使用 dispatch_once。但我仍然更喜欢例外以增加清晰度。如果您在代码中看到 [MyGizmoClass sharedManager][[MyGizmoClass alloc] init] 的混合使用,那会令人困惑。也可能意味着使用 alloc/init 结构的程序员不知道他得到了一个单例,这可能会产生各种讨厌的影响。
    猜你喜欢
    • 2012-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-08
    • 2015-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多