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 类的动态方法)中,超类是 NSObject。 NSObject 的 class 方法在使用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。所以super
和self
都在+methods 中定义。 super
向 MyGizmoClass 的超类发送消息,但是当从 + 方法调用时 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.) 您可以轻松验证 super
与 self
不同。如果您实现[self allocWithZone:NULL]
,您的代码将调用MyGizmoClass 的allocWithZone
实现并无限循环。使用[super allocWithZone:NULL]
,调用超类的版本。
【讨论】:
以上是关于super allocWithZone 对单例类概念有一些疑问的主要内容,如果未能解决你的问题,请参考以下文章
使用partialMockForObject做单例类mock,如何创建像“removeAllExpectations”这样的方法来删除所有记住的?