super allocWithZone 对单例类概念有一些疑问

Posted

技术标签:

【中文标题】super allocWithZone 对单例类概念有一些疑问【英文标题】:super allocWithZone having some doubts in singleton class concept 【发布时间】:2012-08-06 10:18:33 【问题描述】:

我是 Objective-C 的新手,我正在尝试基于 Apple's documentation 创建一个单例类。

+ (MyGizmoClass*)sharedManager

    if (sharedGizmoManager == nil) 
        sharedGizmoManager = [[super allocWithZone:NULL] init];
    
    return sharedGizmoManager;


+ (id)allocWithZone:(NSZone *)zone

    return [[self sharedManager] retain];

在这段代码中,sharedManager 是一个静态方法,它将检查此类的对象是否存在。如果是,它将返回先前创建的对象,否则将创建一个新对象。

我有一些问题:

    如果sharedManager是静态的,如何访问super

    当我打印[super class] 时,为什么它给出了当前的类名?

    为什么[[super allocWithZone:NULL] init]返回的是当前类对象?

    如果这里super 等于self,那么为什么它不调用当前类的allocWithZone:(NSZone *)zone

【问题讨论】:

你可以去my previous post about singleton pattern implementation看看。如果在实现代码中放置了很多 cmets。希望这会有所帮助 你为什么要分配super?你不想要一个 self 的实例吗? 那是一份非常古老的文件。 【参考方案1】:

其他答案虽然指出了有关单身人士的良好信息,但实际上并没有回答您的问题。您的问题实际上主要基于面向对象,您专门引用单例的事实是偶然的。

    我参考self回答this question,这是答案的转述,重要部分

    super 在类级别上下文中确实有意义,但它指的是超类本身,而不是实例

    这个也让我失望了。我问了this question,得出结论:

    [super class] 在当前实例(即self)上调用super 方法。如果 self 有一个覆盖的版本,那么它会被调用并且看起来会有所不同。由于您不覆盖它,因此调用[self class] 与调用[super class] 相同。

    您确定它实际上是在返回这个类的一个实例吗?还是您将其分配给此类的实例sharedGizmoManager

    Super 不等于 self,而是您调用的某些方法:例如[super class] 正在调用 [self class] 将调用的方法的相同实现。

【讨论】:

我认为#2 没有达到目的。 super 是一个不能被覆盖的关键字,它向超类发送消息。在静态 + 方法(不是真正的静态方法,而是 Class 类的动态方法)中,超类是 NSObjectNSObjectclass 方法在使用 super 调用时返回子类,因此 [self class]==[super class] 在静态方法中。 NSObject 的实现如下所示:+(Class)class return self; -- 在这种情况下,self 是子类,因为这是您发送消息的地方。 +(Class)superclass 会有想要的效果,在这种情况下你需要[self superclass];(注意+方法)。 @RiazRizvi,请随时修改答案! 好的,我添加了答案。【参考方案2】:

这里有几件事需要考虑。首先,Cocoa Fundamentals 指南有些过时了。它没有考虑 Apple 开发的一些当前技术,例如 Grand Central Dispatch 和自动引用计数。 allocWithZone 方法中的 [retain] 无法在启用 ARC 的项目中正确编译(因为您是 Obj-C 的新手,我在这里假设您也是 ios/iPhone 的新手,所以您应该阅读这两种技术)。

在这个线程中对不同的单例设计模式进行了很好的讨论: What should my Objective-C singleton look like?

但是,这是一个较旧的线程,因此没有考虑自动引用计数(我多年来一直使用 Louis Gerbang 的答案,它不再适用于启用 ARC 的项目)。

对于启用 ARC 的项目/文件(是的,ARC 可以仅对单个文件启用)- 我们已经转移到使用 GCD 并且运行良好的单例:

static YourClassName * volatile sharedInstance = nil;

+ (YourClassName *)sharedInstance

    static dispatch_once_t sharedInstanceToken;
    dispatch_once(&sharedInstanceToken, ^
        sharedInstance = [[YourClassName alloc] init];
    );
    return sharedInstance;

这里发生了什么?好吧,如果您查看GCD docs,您会看到 dispatch_once 在应用程序的整个生命周期中只针对特定对象执行一次。正如文档所说,这对于以线程安全的方式创建单例非常有用。

最重要的是,我们将 sharedInstance 方法声明为 volatile,这意味着编译器/运行时不应尝试缓存对该方法的调用,而应始终执行其中的代码。这确保我们总是调用 GCD 并且总是取回我们应该得到的对象。

由于您是 Obj-C 的新手,所以我在掩饰一大堆,但肯定要看看 GCD、ARC,然后一旦您在 Obj-C 中有扎实的基础,就可以研究 IMP 缓存,这就是 volatile 阻止发生的事情。

【讨论】:

很好的答案,我最近改用这种创建单例的方式,但我从未听说过 volatile 这个词! 虽然你提供了关于单例的很好的信息,但他们只是偶尔引用它们。问题实际上是关于 oop 以及调用了哪些方法的实现。 “我们将 sharedInstance 方法声明为 volatile” - 但是您声明的是静态变量 volatile,而不是方法。 Objective-C 中不存在可变方法,并且方法调用永远不会被优化掉。相反,您应该在方法内移动静态变量。这保证了它在不调用该方法的情况下永远不会被访问。【参考方案3】:

(1.) '静态函数'中的super 是什么?在 Objective-C 中,+方法被正确地称为 类方法,-方法被称为 实例方法。这些+方法不是真正的静态方法,因为Objective-C classes are themselves objects of an opaque type called Class。所以superself 都在+methods 中定义。 superMyGizmoClass 的超类发送消息,但是当从 + 方法调用时 super 会查找等效的 + 方法,而从 - 方法调用时 super 会查找对应的 - MyGizmoClass 的 +methods 中的self 返回 MyGizmoClass,它是一个 Class,而在 -methods self 中是一个指向 MyGizmoClass 实例的指针。

(2.) 方法+class 返回self.isa。是的[super class] 调用超类的:+class 方法,但是,当self 被传递给+方法时,它的值和类型都不会被修改(而self 的类型是强制转换的通过 -methods 传递给超类)。所以当+class 方法的实现在链上请求self.isa 时,它会得到相同的值,MyGizmoClass。 可以肯定的是,您可以通过从 MyGizmoSuperClass 派生 MyGizmoClass 来验证 super 确实在超类中调用 +class,您可以在其中放置覆盖: p>

    @interface MyGizmoSuperClass : NSObject
    @end
    @implementation MyGizmoSuperClass
    +(Class) class 
        NSLog(@"yes it calls MyGizmoSuperClass:class");
        return [super class];
    
    @end
    @interface MyGizmoClass : MyGizmoSuperClass 
    +(Class) classviasuper;
    @end
    @implementation MyGizmoClass
    +(Class) classviasuper 
        return [super class]; //which version of class will super call?
    
    @end
    int main(int argc, char *argv[])
    
       NSLog(@"returned %@",[MyGizmoClass classviasuper]); 
    

打印

是的,它调用 MyGizmoSuperClass:class 返回 MyGizmoClass

(3.) super 再次调用 allocWithZone 的超类版本,但传递给方法的 self 值仍然指向 MyGizmoClass,并且由于 allocWithZone 返回一个对象接收者的类,你会得到一个MyGizmoClass

(4.) 您可以轻松验证 superself 不同。如果您实现[self allocWithZone:NULL],您的代码将调用MyGizmoClassallocWithZone 实现并无限循环。使用[super allocWithZone:NULL],调用超类的版本。

【讨论】:

以上是关于super allocWithZone 对单例类概念有一些疑问的主要内容,如果未能解决你的问题,请参考以下文章

使用partialMockForObject做单例类mock,如何创建像“removeAllExpectations”这样的方法来删除所有记住的?

SpringBoot(11)-单例中使用AutoWired

在快速测试用例中为单例目标 c 类注入依赖项

创建单例的正确姿势

Java 单例和同步

单例模式