0
votes

I have a project using Core Data. It has a Note object which has one transformable attribute called content. I am storing an NSAttributedString into this property. The property saves fine for the first save, but when I try to create a second note object, the app crashes.

Here is the code I use to save:

- (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];
}

Again this code saves with no problem when there are no objects in the persistent store.
When I attempt to save a new object (in a non empty store:) this exception is thrown first (I have a break point set for all exceptions) -[NSConcreteMutableAttributedString compare:]: unrecognized selector sent to instance 0x1759e8b0

Then when I continue, I get this:

2013-07-18 10:17:39.011 Meta[2417:60b] CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[NSConcreteMutableAttributedString compare:]: unrecognized selector sent to instance 0x1759e8b0 with userInfo (null) 2013-07-18 10:17:39.014 Meta[2417:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteMutableAttributedString compare:]: unrecognized selector sent to instance 0x1759e8b0'

I am also using an NSManagedObject subclass which is implemented as follows:

@interface Note : NSManagedObject

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

Again content is where I am attempting to store the attributed string

I have tried using the default NSValueTransformer and my own subclass. Both cause the same problem.

Edit: Here is the implementation of my value transformer:

#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;
}

Attribute Setup

Edit: Here is the backtrace:

(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
1
It would be useful to see what the backtrace looks like when the "unrecognized selector" exception is thrown. That's most likely the core problem, and the second exception is a side effect of the first. Also, it might help to see what your value transformer looks like.Tom Harrington
Added the value transformer implantation and backtrace.Myron Slaw
I get the same results whether I use the value transformer or let Core Data use the default.Myron Slaw
I'm guessing something in the fetchedResultsController implementation trying to call compare: on that attributed string when sorting the tableView (which is in the view before this one on the navigation stack). The sort descriptor rather.Myron Slaw
Sounds like a good guess, what does the sort descriptor look like? It probably works the first time because with one object there's no sorting, but with two or more the descriptor comes into play.Tom Harrington

1 Answers

1
votes

Found it:

* 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

As the save happened, the fetched results controller started to update the tableview (which was on the presenting view controller).

I have a tableview controller subclass that I use for easy setup that takes a sort descriptor key (as a string) and internally turns it into an array and sets up the fetch request.

[self setSortDescriptorKey:@"content"];

Content is my transformable attribute in the data model (Transforms a NSAttributedString). Originally it was just a normal string, but I needed to store attribute data as well. After changing it, the FRC was sending compare to it. I fixed it by changing the sort descriptor key:

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