【问题标题】:+initialize called more than once+initialize 多次调用
【发布时间】:2013-01-01 12:21:41
【问题描述】:

我有一个+initialize 方法被多次调用,但我不明白为什么。

根据文档,它将为每个类(以及子类)调用一次,

这是我正在使用的代码:

@interface MyClass : NSObject

@end

static NSArray *myStaticArray;

@implementation MyClass

+ (void)initialize
{
  myStaticArray = [NSArray array];
}

@end

(显然还有其他代码,但这是相关部分)。

MyClass 没有子类。它没有做任何花哨的事情。 +initialize 在我的应用程序启动时被调用一次(NSApplication 的委托告诉它用磁盘中的数据填充 myStaticArray)。然后第二次调用 +initialize,即用户第一次选择与此类相关的菜单项。

我只是在我的initialize 代码周围添加了dispatch_once(),这显然解决了我的问题。但我不明白这是怎么回事?为什么在没有子类的情况下会多次调用?

这是第一次调用 +initialize 时的堆栈跟踪:

+[MyClass initialize]
_class_initialize
objc_msgSend
-[MyAppDelegate applicationDidBecomeActive:]
_CFXNotificationPost
NSApplicationMain
main
start

这是第二次调用:

+[MyClass initialize]
_class_initialize
NSApplicationMain
main
start

如您所见,我的代码似乎没有触发对 +initialize 的第二次调用(堆栈跟踪中没有任何内容)。它立即发生之后我显示一个窗口,该窗口显示由+initialize 清除的静态数组的内容(窗口显示数组内容,但紧接着数组为空)。

【问题讨论】:

  • 您可以添加NSLog(@"class=%@", NSStringFromClass([self class])); 并发布输出。
  • @trojanfoe 谢谢,我应该早点尝试。第二次是由NSKVONotifying_MyClass 触发的,直到现在我才听说过。似乎KVO 自动创建了某些对象的子类?我有一个NSTableView 显示这个类的一些对象(通过NSArrayController)。有人知道更多吗?我找不到太多文档。
  • 讨论了动态创建的子类here,但这不是实际文档。无论如何,希望它有所帮助。
  • @bbum 来源是 Key Value Observing。我有一个绑定到数组控制器的表视图,该控制器绑定到包含此类对象的数组。堆栈跟踪中没有任何用处,它在 NSApplicationMain() 内部,但在 self 上执行 NSLog 会显示类名:NSKVONotifying_MyClass。在我展示包含表格视图的窗口后不久它就会被调用(不是展示它的一部分。在那之后)。

标签: objective-c cocoa


【解决方案1】:

+initialize 将在第一次被引用(通过消息)时发送到每个类,包括动态创建的类。运行时中没有针对多次触发执行的保护。如果一个子类被初始化,但没有实现+initialize,无论是super,链上的任何一个都会被再次调用。

正交地,自动 KVO 是通过创建观察实例的类的动态派生子类来实现的。该子类是+initialized,就像任何其他类一样,因此会触发父类的+initialize 的多次执行。

运行时可以采取措施防止这种情况发生。然而,由于 +initialize 一直被记录为可能被多次执行,因此增加的复杂性(考虑到 KVO 类可能频繁地来来去去,这令人惊讶地复杂)被认为不值得付出努力。

目前推荐的模式是:

+ (void) initialize
{
      static dispatch_once_t once;
      dispatch_once(&once, ^{
        ... one time initialization here ...
      });
}

【讨论】:

    【解决方案2】:

    +initialize 被继承链上的每个类调用,所以如果你初始化两个共享同一个超类(或超类及其子类之一)的类,那么超类的 +initialize 方法将被调用两次.

    可能是这个原因吗?

    【讨论】:

    • 父类是NSObject,没有子类。
    【解决方案3】:

    1.Runtime在子类之前调用​​超类中的+initialize方法

    2.如果子类没有方法,则调用父类的初始化方法。

    3.使用总是编年史的方式初始化方法 +(void)初始化{

    if(self==[Car Class]){
    
    //initialize here your static var
    }  
    }
    

    为了清楚的理解阅读这篇文章+(void)initialize

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-10
      • 1970-01-01
      • 2020-11-22
      • 1970-01-01
      • 2014-03-28
      • 2012-05-17
      • 2012-04-04
      相关资源
      最近更新 更多