【问题标题】:Implementation of -init vs. +initialize-init 与 +initialize 的实现
【发布时间】:2014-02-07 15:18:51
【问题描述】:

谁能解释为什么我们需要在+initialize 方法中包含if (self == SomeClass class)

我发现了类似的问题(如下所列),但没有找到任何具体的说明:

  1. Objective-C: init vs initialize
  2. Should +initialize/+load always start with an: if (self == [MyClass class]) guard?

每个人都说,如果你不在 Subclass 中实现/覆盖+initialize,那么它将调用父类两次。

谁能特别解释那部分,特别是为什么它两次调用父类?

最后,为什么我们在继承自NSObject的类中实现+initialize时不会发生这种情况,我们在其中创建了一个自定义的-init方法并调用self = [super init];

【问题讨论】:

  • init 和 initialize 没有任何关系。
  • @rmaddy 在-init 里面你有self = [super init];,它调用了父母的+initialize,还是我不正确?
  • 你不正确。在调用任何其他类或实例方法之前,每个类都会调用一次 initialize。
  • @rmaddy 这就是我想知道并要求澄清的原因。它什么时候被调用?如果我不包含 if 语句,它如何被调用两次。

标签: objective-c initialization init


【解决方案1】:

您引用的问题有很好的公认答案。总而言之,+initialize 由运行时在每个类上调用,因此对于具有 N 个子类的超类,它将在超类上被调用 N+1 次(直接调用一次,每个继承它的子类调用一次)。如果子类覆盖它并调用 super,也是一样。

您可以通过在超类级别询问“这是我的系统直接初始化,而不是 'super' 被我的子类继承或调用吗?”来防御这种情况?

if (self == [ThisSuperclass self]) {}

-init 用于初始化类的实例,默认情况下不会像+initialize 那样被运行时调用。实例继承其-init的实现,可以覆盖继承的实现,也可以通过调用[super init];享受继承的实现的好处。

【讨论】:

  • 所以+initialize 在任何情况下都会被运行时调用,而无需用户编写代码?如果是,我想知道在什么情况下会在子类中调用 super 上的 +initialize
  • @danh,不仅当子类调用super时,该方法会被调用两次。当子类没有实现+initialize 时也是如此,这是迄今为止最常见的情况。
【解决方案2】:

-init+initialize 是完全不同的东西。第一个是初始化instances;第二个是初始化

第一次向任何给定类发送消息时,运行时确保在它及其超类上调用+initialize。超类首先被初始化,因为它们需要在任何子类初始化之前准备好。

所以,YourSubclass 第一次收到消息时,运行时可能会执行以下操作:

[NSObject initialize];
[YourClass initialize];
[YourSubclass initialize];

(虽然这不太可能是第一次向NSObject 发送消息,因此此时可能不需要对其进行初始化。这只是为了说明。)

如果YourSubclass 没有实现+initialize,那么上面显示的[YourSubclass initialize] 调用实际上将调用+[YourClass initialize]。这只是正常工作的继承机制。这将是第二次调用+[YourClass initialize]

由于在+initialize 方法中完成的工作通常是应该只做一次的事情,所以if (self == [TheClassWhoseImplementationThisMethodIsPartOf class]) 的守卫是必要的。此外,该工作通常假设 self 指的是当前正在编写的类,因此这也是使用守卫的原因。

您引用的第二个答案指出了一个例外,这是使用 +setKeys:triggerChangeNotificationsForDependentKey: 方法注册 KVO 相关键的旧式机制。该方法特定于调用它的实际类,而不是任何子类。你应该避免它并使用更现代的+keyPathsForValuesAffectingValueForKey:+keyPathsForValuesAffecting<Key> 方法。如果您必须使用旧方式,请将那部分放在防护装置之外。此外,此类的子类必须调用super,这通常不会这样做。

更新:

+initialize 方法通常不应调用到super,因为运行时已经初始化了超类。当且仅当已知超类使用旧机制注册依赖键时,任何子类都必须调用 super。

-init 的情况不存在同样的担忧,因为运行时不会在调用您的方法之前自动为您调用超类的 init 方法。实际上,如果您的 init 方法没有调用到super,那么就不会初始化实例的超类的“部分”。

【讨论】:

  • 在你的例子中 TheClassWhoseImplementationThisMethodIsPartOfYourClass 吗?
  • +[YourClass initialize]的实现里面,是的。我试图让它通用。
【解决方案3】:

假设您有一个实现+initialize 的超类和一个没有实现的子类。

@interface SuperClass : NSObject @end
@implementation SuperClass
+(void)initialize {
    NSLog(@"This is class %@ running SuperClass +initialize", self);
}
@end

@interface SubClass : SuperClass @end
@implementation SubClass
// no +initialize implementation
@end

使用超类。这会引发对 +[SuperClass initialize] 的调用。

[SuperClass class];
=> This is class SuperClass running SuperClass +initialize

现在使用子类。运行时在SubClass 中寻找+initialize 的实现,但没有找到任何东西。然后它在SuperClass 中查找继承的实现并找到它。即使已经代表SuperClass 本身调用了一次继承的实现,也会调用它:

[SubClass class];
=> This is class SubClass running SuperClass +initialize

守卫允许您执行最多只能运行一次的工作。对+initialize 的任何后续调用都具有与self 不同的类,因此守卫可以忽略它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-17
    相关资源
    最近更新 更多