将可转换的 NSAttributedString 保存到 Core Data 时出现异常奇怪的崩溃

Posted

技术标签:

【中文标题】将可转换的 NSAttributedString 保存到 Core Data 时出现异常奇怪的崩溃【英文标题】:Extremely Odd Crash When saving a transformable NSAttributedString into Core Data 【发布时间】:2013-07-18 14:36:28 【问题描述】:

我有一个使用 Core Data 的项目。它有一个 Note 对象,该对象具有一个称为内容的可转换属性。我将 NSAttributedString 存储到此属性中。该属性在第一次保存时保存得很好,但是当我尝试创建第二个笔记对象时,应用程序崩溃了。

这是我用来保存的代码:

- (IBAction)save:(id)sender 


    Note* newNote = [NSEntityDescription insertNewObjectForEntityForName:@"Note"     inManagedObjectContext:[self managedObjectContext]];
    NSAttributedString* string = [[self textView]attributedText];

    [newNote setContent:string];
    NSArray* tokens = [[self tokenField]tokens];

    NSError* error = nil;

    if (![[self managedObjectContext]save:&error]) 
        NSLog(@"Error saving %@",[error localizedDescription]);
    
    [[self navigationController]popViewControllerAnimated:YES];

再次,当持久存储中没有对象时,此代码可以毫无问题地保存。 当我尝试保存新对象时(在非空存储中:)首先抛出此异常(我为所有异常设置了断点) -[NSConcreteMutableAttributedString compare:]:无法识别的选择器发送到实例 0x1759e8b0

然后当我继续时,我得到了这个:

2013-07-18 10:17:39.011 Meta[2417:60b] CoreData:错误:严重的应用程序错误。在核心数据更改处理期间捕获到异常。这通常是 NSManagedObjectContextObjectsDidChangeNotification 观察者中的一个错误。 -[NSConcreteMutableAttributedString compare:]: 无法识别的选择器使用 userInfo (null) 发送到实例 0x1759e8b0 2013-07-18 10:17:39.014 Meta[2417:60b] *** 由于未捕获的异常“NSInvalidArgumentException”而终止应用程序,原因:“-[NSConcreteMutableAttributedString 比较:]:无法识别的选择器发送到实例 0x1759e8b0”

我也在使用一个 NSManagedObject 子类,它的实现方式如下:

@interface Note : NSManagedObject

@property (nonatomic, strong) id content;
@end

内容是我试图存储属性字符串的地方

我尝试过使用默认的 NSValueTransformer 和我自己的子类。两者都会导致相同的问题。

编辑:这是我的价值转换器的实现:

#import "AttributedStringValueTransformer.h"

@implementation AttributedStringValueTransformer
+(Class)transformedValueClass 
    return [NSAttributedString class];

+(void)initialize 
    [NSValueTransformer setValueTransformer:[[self alloc]init] forName:@"NSAttributedStringValueTransformer"];


+(BOOL)allowsReverseTransformation 
    return YES;


-(NSData*)transformedValue:(NSAttributedString*)value 
    NSData* stringAsData = [NSKeyedArchiver archivedDataWithRootObject:value];
    return stringAsData;


-(NSAttributedString*)reverseTransformedValue:(NSData*)value 
    NSAttributedString* string = [NSKeyedUnarchiver unarchiveObjectWithData:value];

    return string;

编辑:这是回溯:

(lldb) bt
* thread #1: tid = 0x5d2ef, 0x39a3d688 libobjc.A.dylib`objc_exception_throw, queue = 'com.apple.main-thread, stop reason = breakpoint 1.1
    frame #0: 0x39a3d688 libobjc.A.dylib`objc_exception_throw
    frame #1: 0x2f958fa2 CoreFoundation`-[NSObject(NSObject) doesNotRecognizeSelector:] + 202
    frame #2: 0x2f95787a CoreFoundation`___forwarding___ + 706
    frame #3: 0x2f8a5528 CoreFoundation`__forwarding_prep_0___ + 24
    frame #4: 0x302a5d48 Foundation`_NSCompareObject + 32
    frame #5: 0x3034fe7e Foundation`-[NSSortDescriptor compareObject:toObject:] + 270
    frame #6: 0x2f79e5f4 CoreData`+[NSFetchedResultsController(PrivateMethods) _insertIndexForObject:inArray:lowIdx:highIdx:sortDescriptors:] + 216
    frame #7: 0x2f79ada6 CoreData`-[NSFetchedResultsController(PrivateMethods) _postprocessInsertedObjects:] + 514
    frame #8: 0x2f79c842 CoreData`-[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 1898
    frame #9: 0x2f89c836 CoreFoundation`_CFXNotificationPost + 1718
    frame #10: 0x302a13b0 Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] + 76
    frame #11: 0x2f72280a CoreData`-[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:] + 78
    frame #12: 0x2f721b0a CoreData`-[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:] + 298
    frame #13: 0x2f6a0af6 CoreData`-[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 2346
    frame #14: 0x2f7159ba CoreData`-[NSManagedObjectContext save:] + 190
    frame #15: 0x00106f82 Meta`-[NewNoteViewController save:](self=0x15dd2030, _cmd=0x325ba35a, sender=0x15dc5a70) + 918 at NewNoteViewController.m:176
    frame #16: 0x320482a2 UIKit`-[UIApplication sendAction:to:from:forEvent:] + 90
    frame #17: 0x32048330 UIKit`-[UIBarButtonItem(UIInternal) _sendAction:withEvent:] + 120
    frame #18: 0x320482a2 UIKit`-[UIApplication sendAction:to:from:forEvent:] + 90
    frame #19: 0x32048242 UIKit`-[UIApplication sendAction:toTarget:fromSender:forEvent:] + 30
    frame #20: 0x32048220 UIKit`-[UIControl sendAction:to:forEvent:] + 44
    frame #21: 0x3217cfce UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 374
    frame #22: 0x32048036 UIKit`-[UIControl touchesEnded:withEvent:] + 590
    frame #23: 0x31f830e0 UIKit`-[UIWindow _sendTouchesForEvent:] + 528
    frame #24: 0x31f718f0 UIKit`-[UIApplication sendEvent:] + 196
    frame #25: 0x32114406 UIKit`_UIApplicationHandleHIDEvent + 6262
    frame #26: 0x306765ce IOKit`__IOHIDEventSystemClientQueueCallback + 222
    frame #27: 0x2f911f04 CoreFoundation`__CFMachPortPerform + 136
    frame #28: 0x2f91d206 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 34
    frame #29: 0x2f91d1a2 CoreFoundation`__CFRunLoopDoSource1 + 346
    frame #30: 0x2f91b966 CoreFoundation`__CFRunLoopRun + 1398
    frame #31: 0x2f892446 CoreFoundation`CFRunLoopRunSpecific + 522
    frame #32: 0x2f89222a CoreFoundation`CFRunLoopRunInMode + 106
    frame #33: 0x343f06da GraphicsServices`GSEventRunModal + 138
    frame #34: 0x31fbae00 UIKit`UIApplicationMain + 1136
    frame #35: 0x00105374 Meta`main(argc=1, argv=0x27d07d1c) + 116 at main.m:16

【问题讨论】:

在抛出“无法识别的选择器”异常时查看回溯的样子会很有用。这很可能是核心问题,第二个例外是第一个例外的副作用。此外,看看你的价值转换器是什么样子可能会有所帮助。 添加了价值转换器植入和回溯。 无论我使用值转换器还是让 Core Data 使用默认值,我都会得到相同的结果。 我猜想在 fetchedResultsController 实现中尝试调用 compare: 在对 tableView 进行排序时在该属性字符串上(在导航堆栈上此视图之前的视图中)。而是排序描述符。 听起来不错,排序描述符是什么样的?它可能是第一次工作,因为一个对象没有排序,但有两个或更多描述符开始发挥作用。 【参考方案1】:

找到了:

* thread #1: tid = 0x5d2ef, 0x39a3d688 libobjc.A.dylib`objc_exception_throw, queue = 'com.apple.main-thread, stop reason = breakpoint 1.1
    frame #0: 0x39a3d688 libobjc.A.dylib`objc_exception_throw
    frame #1: 0x2f958fa2 CoreFoundation`-[NSObject(NSObject) doesNotRecognizeSelector:] + 202
    frame #2: 0x2f95787a CoreFoundation`___forwarding___ + 706
    frame #3: 0x2f8a5528 CoreFoundation`__forwarding_prep_0___ + 24
    frame #4: 0x302a5d48 Foundation`_NSCompareObject + 32
    frame #5: 0x3034fe7e Foundation`-[NSSortDescriptor compareObject:toObject:] + 270**
    frame #6: 0x2f79e5f4 CoreData`+[NSFetchedResultsController(PrivateMethods) _insertIndexForObject:inArray:lowIdx:highIdx:sortDescriptors:] + 216
    frame #7: 0x2f79ada6 CoreData`-[NSFetchedResultsController(PrivateMethods) _postprocessInsertedObjects:] + 514
    frame #8: 0x2f79c842 CoreData`-[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:] + 1898

当保存发生时,获取的结果控制器开始更新 tableview(在呈现视图控制器上)。

我有一个 tableview 控制器子类,用于轻松设置,它采用排序描述符键(作为字符串)并在内部将其转换为数组并设置获取请求。

[self setSortDescriptorKey:@"content"];

Content 是我在数据模型中的可转换属性(转换 NSAttributedString)。 最初它只是一个普通的字符串,但我还需要存储属性数据。更改后,FRC 正在发送比较它。我通过更改排序描述符键来修复它:

[self setSortDescriptorKey:@"content.string"];

【讨论】:

以上是关于将可转换的 NSAttributedString 保存到 Core Data 时出现异常奇怪的崩溃的主要内容,如果未能解决你的问题,请参考以下文章

将 HTML 字符串转换为 NSAttributedString 时运行存档时崩溃

无法将 NSAttributedString.DocumentAttributeKey 类型的值转换为 .DocumentReadingOptionKey

将 HTML 转换为属性字符串时 NSAttributedString 崩溃

Objective C,将 html 转换为 NSAttributedString 并禁用超链接

NSAttributedString可以强制转换为NSMutableAttributedString类型吗?下面这代码有什么问题 为什么报错

NSFontAttributeName 未应用于 NSAttributedString