核心数据:EXC_BAD_ACCESS 访问关系

Posted

技术标签:

【中文标题】核心数据:EXC_BAD_ACCESS 访问关系【英文标题】:Core Data: EXC_BAD_ACCESS accessing relationship 【发布时间】:2013-12-06 22:07:21 【问题描述】:

我在一个简单的 Core Data 项目中遇到了崩溃。

我使用 Core Data 创建了一个新的空 iPhone 项目。两个实体以一对一的反向关系添加到数据模型中。两者都有自动生成的 NSManagedObject 子类。实体名称和类都设置在两者上。

DBMove
  attribute: moveValue:Integer16
  relationship: newPosition -> DBPosition (inverse: move)

DBPosition 
  attribute: positionValue:Integer16
  relationship: move -> DBMove (inverse: newPosition)

有一个创建 DBMove 和 DBPosition 的自定义视图控制器。然后它设置它们之间的反比关系并保存它。从应用程序委托中检索托管对象上下文;我认为这是安全的,因为 [NSThread isMultiThreaded] 返回 false。

- (void)newEntries

     AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
     NSManagedObjectContext *context = appDelegate.managedObjectContext;

     DBPosition *newPos = [NSEntityDescription
                      insertNewObjectForEntityForName:@"DBPosition"
                      inManagedObjectContext:context];
     newPos.positionValue = [NSNumber numberWithInt:5];

     DBMove *newMove = [NSEntityDescription
                   insertNewObjectForEntityForName:@"DBMove"
                   inManagedObjectContext:context];
     newMove.moveValue = [NSNumber numberWithInt:2];

     newPos.move = newMove;

     NSError *error;
     if (![context save:&error]) 
         NSLog(@"Save error: %@", [error localizedDescription]);
    

然后视图控制器获取所有 DBMove。它们的 Integer16 属性按预期输出。 [编辑] 访问关系会导致 main::@autoreleasepool 中的 EXC_BAD_ACCESS 在循环遍历 所有 获取结果后的某个时间发生。在崩溃之前,之前程序中数据库中的所有 DBMove 和 DBPosition 条目都可以正确打印,然后是我的 ViewController init 中的 NSLogs;崩溃似乎延迟了。大约每 5 次程序运行中出现 4 次。 [结束编辑]

- (void)printMoves

     AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
     NSManagedObjectContext *context = appDelegate.managedObjectContext;

     NSError *error;
     NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
     NSEntityDescription *entity = [NSEntityDescription entityForName:@"DBMove"
                                          inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
     NSArray *fetchedObjects = [context executeFetchRequest:fetchRequest error:&error];

     for (DBMove *move in fetchedObjects) 
         DBPosition *newPos = move.newPosition; // <--- all's well without this reference!
         NSLog(@"Move     : %d", [move.moveValue intValue]);
         NSLog(@"Position : %d", [newPos.positionValue intValue]);
    

NSZombieEnabled=YES 使程序正常运行并打印预期值。在整个执行过程中,我的上下文似乎是相同的。

这是堆栈跟踪:

#0  0x018300b2 in objc_msgSend ()
#1  0x00256ffc in -[_CDSnapshot dealloc] ()
#2  0x0025573d in -[_CDSnapshot release] ()
#3  0x00260384 in -[NSManagedObject(_NSInternalMethods) _clearRawPropertiesWithHint:] ()
#4  0x0026018b in -[NSFaultHandler turnObject:intoFaultWithContext:] ()
#5  0x002607ba in -[NSManagedObject dealloc] ()
#6  0x00257b88 in -[_PFManagedObjectReferenceQueue _processReferenceQueue:] ()
#7  0x00232fe1 in _performRunLoopAction ()
#8  0x01a654ce in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()
#9  0x01a6541f in __CFRunLoopDoObservers ()
#10 0x01a43344 in __CFRunLoopRun ()
#11 0x01a42ac3 in CFRunLoopRunSpecific ()
#12 0x01a428db in CFRunLoopRunInMode ()
#13 0x038fe9e2 in GSEventRunModal ()
#14 0x038fe809 in GSEventRun ()
#15 0x0058ed3b in UIApplicationMain ()
#16 0x0000396d in main at /Users/brian/devel/test2-CoreData/test2-CoreData/main.m:16

当我在空的新项目上检查“核心数据”时,所有核心数据设置都来自标准包含。

大概我过度释放了一个托管对象,但是在哪里?

【问题讨论】:

你的代码看起来不错...这可能是别的东西。您是否尝试将应用程序从模拟器上擦除并重新安装? 不是检查多线程,而是检查线程是主线程。如下, if ([NSThread currentThread] == [NSThread mainThread]) 关系通常是NSSet - 因此将DBMove 对象分配给一组可能是问题所在(尽管我预计会出现与您在上面显示的错误消息不同的错误消息)。跨度> @Mundi,我每隔几次运行就删除模拟器上的应用程序,选择 Product->Clean,然后重建。我同意你的评价。 @Arun,感谢主线程代码。它在这两种方法以及创建上下文、持久存储协调器和托管对象模型的应用程序委托方法中报告主线程。 【参考方案1】:

以“new”开头的属性newPosition 是问题所在!它被翻译成一个带有名为“newPosition”的访问器的属性,这在Transitioning to ARC Release Notes 中是被禁止的:

为了允许与手动保留释放代码的互操作,ARC 强加了一个 对方法命名的约束:

您不能为访问者指定以 new 开头的名称。这反过来 意味着你不能,例如,声明一个名字 以 new 开头,除非您指定不同的 getter:

// Won't work:
@property NSString *newTitle;

// Works:
@property (getter=theNewTitle) NSString *newTitle;

可以像示例中那样编写自定义 getter,但选择更好的名称并继续使用内置工具生成类文件要容易得多。

请注意,编译器会在使用 @synthesize 而不是 @dynamic 时捕获此错误,就像在自动生成的 NSManagedObject 类中使用的那样。

【讨论】:

因此,在过渡到 ARC 时,还必须迁移核心数据属性/关系名称。您可以向 Apple 提交错误报告,要求在他们的自动 ARC 转换工具中进行此操作。很酷的发现! 令我震惊的是,在 2021 年,至少没有警告说您的以“新”开头的属性会导致崩溃。 @Shawn 我已经很多年没有认真接触过 Obj-C 了,但现在还是这样吗?我猜 Core Data+Obj-C+"new..." 的话题还不够热门,不足以让 Apple 上榜。 这发生在我上周的 Swift 5 中,而不是 Obj-C。

以上是关于核心数据:EXC_BAD_ACCESS 访问关系的主要内容,如果未能解决你的问题,请参考以下文章

访问核心数据时的 EXC_BAD_ACCESS KERN_INVALID_ADDRESS

尝试访问托管对象属性时的 EXC_BAD_ACCESS

EXC_BAD_ACCESS:核心数据、NSFetchedResultsController 和 numberOfRowsInSection

非零整数值的核心数据 EXC_BAD_ACCESS

Index Beyond Bounds / EXC_BAD_ACCESS 核心数据删除 SwiftUI

核心数据 executeFetchRequest 因 exc_bad_access 而失败