7

我是 Objective-C 的新手,我正在尝试根据Apple 的文档创建一个单例类。

+ (MyGizmoClass*)sharedManager
{
    if (sharedGizmoManager == nil) {
        sharedGizmoManager = [[super allocWithZone:NULL] init];
    }
    return sharedGizmoManager;
}

+ (id)allocWithZone:(NSZone *)zone
{
    return [[self sharedManager] retain];
}

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

我有一些疑问:

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

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

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

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

4

3 回答 3

3

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

在这个线程中对不同的单例设计模式进行了很好的讨论: 我的 Objective-C 单例应该是什么样子?

然而,这是一个较旧的线程,因此没有考虑自动引用计数(我多年来一直使用 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 文档,您会发现 dispatch_once 在特定对象的应用程序的整个生命周期中只执行一次。正如文档继续说的那样,这对于以线程安全的方式创建单例非常有用。

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

因为你是 Obj-C 的新手,所以我在掩饰一堆,但肯定要看看 GCD、ARC,然后一旦你在 Obj-C 中有扎实的基础,就会研究 IMP 缓存,这就是volatile 正在防止发生。

于 2012-08-06T11:28:54.330 回答
3

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

  1. 我参考回答了这个问题self,这是答案的转述的重要部分

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

  2. 这个也让我失望了。我问了这个问题,得出的结论是:

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

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

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

于 2012-08-06T12:29:25.673 回答
3

(1.) super“静态函数”中有什么?在 Objective-C 中,+方法被恰当地称为类方法,-方法被称为实例方法。这些 +methods 不是真正的静态方法,因为Objective-C 类本身就是一个名为Class的不透明类型的对象。所以superself都在 + 方法中定义。superMyGizmoClass的超类发送消息,但是当从 +method 调用时super查找等效的 +method,而从 -method 调用时super查找相应的 -method。在MyGizmoClass
self的 +methods 中返回MyGizmoClass这是一个,而 in -methodsself是指向MyGizmoClass实例的指针。

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

    @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调用 的超类版本,allocWithZoneself传递给方法的值仍指向MyGizmoClass,并且由于allocWithZone返回接收者类的对象,因此您将返回MyGizmoClass

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

于 2013-08-05T21:17:25.947 回答