为啥 Core Data 托管对象的 +initialize 方法会被调用两次?

Posted

技术标签:

【中文标题】为啥 Core Data 托管对象的 +initialize 方法会被调用两次?【英文标题】:Why is the +initialize method of Core Data managed objects being called twice?为什么 Core Data 托管对象的 +initialize 方法会被调用两次? 【发布时间】:2014-02-10 16:13:22 【问题描述】:

我有一个带有+initialize 方法的托管核心数据对象。 (如果重要的话,我希望使用该方法来初始化一个设置成本很高的静态变量。)在测试该初始化代码时,我惊讶地发现 +initialize 方法被调用了两次。

initialize 第一次被称为self 被定义为(Class) MyClass,正如我所料。

initialize 第二次被称为 self 被定义为 (Class) MyClass_MyClass_,这让我怀疑 Core Data 托管对象的某种异常初始化。

虽然这对我来说没有问题(我可以测试一下静态变量是否已经初始化,无论如何我都会这样做来处理子类化),但它让我怀疑 Core Data 正在做某事在我不理解的对象或类生命周期中。有人可以向我解释这个 MyClass_MyClass_ +initialize 方法调用中发生了什么吗?

【问题讨论】:

澄清一下,我理解+initialize,似乎Core Data的实现是在幕后创建对象的子类。我想知道那个子类在做什么。 【参考方案1】:

Core Data 属性的访问器方法是在运行时动态创建的。 这通过创建一个子类MyClass_MyClass_MyClass)并添加 子类的必要方法。

Core Data 也做了一些技巧来隐藏事实 你的实体的对象实际上是子类的实例:

e = [NSEntityDescription insertNewObjectForEntityForName:@"MyClass" inManagedObjectContext:context];
NSLog(@"%@", [e class]);               // --> MyClass
NSLog(@"%s", object_getClassName(e));  // --> MyClass_MyClass_

如果您不为您的实体设置自定义类,它可能会变得更加明显。 在这种情况下,输出是

NSLog(@"%@", [e class]);               // --> NSManagedObject
NSLog(@"%s", object_getClassName(e));  // --> NSManagedObject_MyClass_

所以e“看起来像”NSManagedObject 的一个实例,但确实是一个实例 动态创建的子类NSManagedObject_<EntityName>_。这个子类 实现所有访问器方法(对于该实体来说是唯一的)。

【讨论】:

谢谢,这正是我的怀疑,但奇怪的是我在任何地方都找不到通过动态创建的子类实现其动态访问器的 Core Data 的引用。在我开始调查之前,我最初假设动态方法被直接混入 MyClass 类。 @DavidOgren:方法调配仅适用于每个类,而不适用于每个实例。如果您有两个不同的实体并且没有分配自定义类,那么通过 swizzling 实现附件将很困难,因为所有对象都是 NSManagedObject 的实例。 - 不幸的是,我没有明确的参考资料表明它是这样工作的。我可能读过一些关于它的东西,但我不记得在哪里。 - 另一个指标可能是当子类初始化时,你会在堆栈回溯中找到类似[NSManagedObject(_PFDynamicAccessorsAndPropertySupport) classForEntity:] 的内容。 是的,这对我来说很有意义。再次感谢。一开始思考这个问题的时候,我忘记了没有自定义类可以调配的情况。【参考方案2】:

来自Apples documentation 上的NSObject 我认为这是正常行为:运行时将初始化发送到程序中的每个类,就在类或从它继承的任何类从程序中发送其第一条消息之前.运行时以线程安全的方式将初始化消息发送到类。超类在其子类之前收到此消息。如果子类没有实现 initialize(运行时将调用继承的实现)或子类显式调用 [super initialize],则超类实现可能会被多次调用。如果你想保护自己免于被多次运行,你可以按照这些思路来构建你的实现......

+ (void)initialize 
    if (self == [ClassName class]) 
    // ... do the initialization ...
   

为什么你不使用 NSManagedObject 提供的 awakeFrom... 方法?

【讨论】:

您可以考虑使用+load,这样不会重复调用。 @Volker,我不太确定 awakeFrom... 方法对我有多大帮助。我错过了什么吗? (另外,我可以使用 +initialize 轻松完成这项工作,这让我很惊讶,我不喜欢不了解额外子类的幕后情况。) @TomHarrington,感谢您的想法。有趣的想法。我认为我会坚持使用初始化,因为很容易确保只初始化一次。 +load 会将初始化推到生命周期的非常非常早的阶段。 @DavidOgren 我更多地考虑了需要计算的某种瞬态数据,因此考虑了诸如awakeFromInsert: 或 `awakeFromFetch: 之类的方法。但我现在有了更多的思考,看看为什么那行不通。 @Volker。感谢您的回答和想法。

以上是关于为啥 Core Data 托管对象的 +initialize 方法会被调用两次?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的节点 JS 应用程序出现 Invalid shorthand property initializer 错误?

引用JS对象中的内部变量[重复]

sessionStorage localStorage存取值的用法

koa和sqlite3节点REST端点仅返回数据库对象而不是数据

为啥 C++ 列表初始化也会考虑常规构造函数?

JS中关于window对象的代码?