Every now and then I get one of the EXC_BAD_ACCESS errors that seem to plague new Objective-C programmers.
In my program I'm trying to get a get the time in seconds, convert that to a string and then convert that to a NSData object for writing to a file. Here is the code I'm using, but it crashes with an EXC_BAD_ACCESS every time I run it. What am I doing wrong?
-(void) startTheClock{
NSTimeInterval cloqInTime = [NSDate timeIntervalSinceReferenceDate];
NSString * dateStr = [self stringFromTimeInterval:cloqInTime];
NSData * data = [[dateStr stringByAppendingString:#", "] dataUsingEncoding:NSUTF8StringEncoding];
NSLog([#"Data:" stringByAppendingString:[data description]]);
// [data retain]; // <-- Uncommenting this and the [data release] line doesn't prevent the error
[self writeData:data]; // <-- EXC_BAD_ACCESS happens here!
// [data release];
}
When I run this method I get the following output:
timeString: |2011-11-04 16:17:12|
Data:<32303131 2d31312d 30342031 363a3137 3a31322c 20>
As requested here is my stringFromTimeInterval method:
-(NSString *) stringFromTimeInterval:(NSTimeInterval)t{
NSDate * date = [NSDate dateWithTimeIntervalSinceReferenceDate:t];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSString *timeString = [dateFormat stringFromDate:date];
NSLog(#"timeString: %#", timeString);
return timeString;
}
Here is the stacktrace:
#0 0x00007fff84407e90 in objc_msgSend ()
#1 0x00000001054543f0 in 0x00000001054543f0 ()
#2 0x00000001000012ee in -[WorqAppDelegate startTheClock] at /Users/slayton/Documents/Xcode/Worq/Worq/WorqAppDelegate.m:61
#3 0x0000000100001164 in -[WorqAppDelegate cloqInAction:] at /Users/slayton/Documents/Xcode/Worq/Worq/WorqAppDelegate.m:37
#4 0x00007fff8cbb9a1d in -[NSObject performSelector:withObject:] ()
#5 0x00007fff86f69710 in -[NSApplication sendAction:to:from:] ()
#6 0x00007fff86f69642 in -[NSControl sendAction:to:] ()
#7 0x00007fff86f6956d in -[NSCell _sendActionFrom:] ()
#8 0x00007fff86f68a30 in -[NSCell trackMouse:inRect:ofView:untilMouseUp:] ()
#9 0x00007fff86fe88e0 in -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] ()
#10 0x00007fff86f6763a in -[NSControl mouseDown:] ()
#11 0x00007fff86f320e0 in -[NSWindow sendEvent:] ()
#12 0x00007fff86eca68f in -[NSApplication sendEvent:] ()
#13 0x00007fff86e60682 in -[NSApplication run] ()
#14 0x00007fff870df80c in NSApplicationMain ()
#15 0x0000000100001092 in main ()
Here is the writeData method, recordFile is an instance of NSFileHandle:
-(BOOL) writeData:(NSData *)data{
if (recordFile != NULL)
[recordFile writeData:data];
else
NSLog(#"Record file is null! No data written");
}
Keep an eye on your recordFile object. I'm wondering if this object has been over-released (and thus deallocated) but is not nil. This would cause the [recordFile writeData:data] method to throw an EXC_BAD_ACCESS. What is the lifespan of recordFile?
Going out on a limb, but I'd say that data is probably nil. Have you checked the value of data?
the debugger and in particular the stack trace is your friend, but yeah, most likely data is nil.
in the debugger window at the prompt, type: po data
or better, just look at the local variables in the left pane and see what data's value is.
Related
Why does the following code work ? It's a small Cocoa program that uses NSOpenPanel to select a file and open it in Emacs.app. It can be run from the command line with the starting directory as an argument.
How does NSOpenPanel run without invoking NSApplication or NSRunLoop ? What are the limitations on a Cocoa program that doesn't explicitly start NSApplication or NSRunLoop ? I would have thought one of them was: you can't use any kind of GUI. Perhaps by invoking NSOpenPanel, some fallback code being called that invokes NSRunLoop ? I put breakpoints on +[NSApplication alloc] and +[NSRunLoop alloc] and they were not triggered.
main.m:
#import <Cocoa/Cocoa.h>
NSString *selectFileWithStartPath(NSString *path) {
NSString *answer = nil;
NSOpenPanel* panel = [NSOpenPanel openPanel];
panel.allowsMultipleSelection = NO;
panel.canChooseFiles = YES;
panel.canChooseDirectories = NO;
panel.resolvesAliases = YES;
if([panel runModalForDirectory:path file:nil] == NSOKButton)
answer = [[[panel URLs] objectAtIndex:0] path];
return answer;
}
int main(int argc, const char * argv[]) {
NSString *startPath = argc > 1 ? [NSString stringWithUTF8String:argv[1]] : #"/Users/Me/Docs";
printf("%s\n", argv[1]);
BOOL isDir;
if([[NSFileManager defaultManager] fileExistsAtPath:startPath isDirectory:&isDir] && isDir) {
system([[NSString stringWithFormat:#"find %# -name \\*~ -exec rm {} \\;", startPath] UTF8String]);
NSString *file = selectFileWithStartPath(startPath);
if(file) [[NSWorkspace sharedWorkspace] openFile:file withApplication:#"Emacs.app"];
}
}
runModalForDirectory:file:types: creates and runs its own event loop.
From the documentation:
Displays the panel and begins a modal event loop that is terminated
when the user clicks either OK or Cancel.
You can see that also if you pause the program while the "Open" dialog is active
and print the stack backtrace in the debugger console:
frame #0: 0x00007fff8b855a1a libsystem_kernel.dylib`mach_msg_trap + 10
frame #1: 0x00007fff8b854d18 libsystem_kernel.dylib`mach_msg + 64
frame #2: 0x00007fff8549f155 CoreFoundation`__CFRunLoopServiceMachPort + 181
frame #3: 0x00007fff8549e779 CoreFoundation`__CFRunLoopRun + 1161
frame #4: 0x00007fff8549e0b5 CoreFoundation`CFRunLoopRunSpecific + 309
frame #5: 0x00007fff88381a0d HIToolbox`RunCurrentEventLoopInMode + 226
frame #6: 0x00007fff883817b7 HIToolbox`ReceiveNextEventCommon + 479
frame #7: 0x00007fff883815bc HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter + 65
frame #8: 0x00007fff838ca3de AppKit`_DPSNextEvent + 1434
frame #9: 0x00007fff838c9a2b AppKit`-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 122
frame #10: 0x00007fff83c28e2e AppKit`-[NSApplication _realDoModalLoop:peek:] + 642
frame #11: 0x00007fff83c2754e AppKit`-[NSApplication runModalForWindow:] + 117
frame #12: 0x00007fff83ef5d0b AppKit`-[NSSavePanel runModal] + 276
frame #13: 0x0000000100000c61 xxx`selectFileWithStartPath(path=0x00000001000010b8) + 225 at main.m:18
frame #14: 0x0000000100000e0d xxx`main(argc=1, argv=0x00007fff5fbff9c0) + 189 at main.m:26
As you can see in frame #11, -[NSApplication runModalForWindow:] is used to run a modal
event loop for the "Open" dialog.
The stack is as below, how to fix this crash ?
It is only in iOS7 and why there is uitableview in the stack?
0 libobjc.A.dylib objc_msgSend + 5
1 UIKit -[UIAlertView(Private) modalItem:shouldDismissForButtonAtIndex:] + 62
2 UIKit -[_UIModalItemsCoordinator _notifyDelegateModalItem:tappedButtonAtIndex:] + 94
3 UIKit -[_UIModalItemAlertContentView tableView:didSelectRowAtIndexPath:] + 894
4 UIKit -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1078
5 UIKit -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 214
6 UIKit _applyBlockToCFArrayCopiedToStack + 316
7 UIKit _afterCACommitHandler + 430
8 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 20
- (id)initWithIdentifier:(LTAlertMsgIdentifier)alertIdentifier
delegate:(id /*<UIAlertViewDelegate>*/)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ... {
LTAlertMsgManager *sharedAlertMsgMgr = [LTAlertMsgManager shareAlertManageInstance];
NSString *strMsg = [sharedAlertMsgMgr getLTAlertMsgByAlertID:alertIdentifier];
if ([NSString isBlankString:strMsg]){
// alert is invalid, if alert message is empty
return nil;
}
NSString *strTitle = [sharedAlertMsgMgr getLTAlertTitleByAlertID:alertIdentifier];
if (self = [super initWithTitle:([NSString isBlankString:strTitle] ? nil : strTitle)
message:strMsg
delegate:delegate
cancelButtonTitle:cancelButtonTitle
otherButtonTitles:nil]){
va_list args;
va_start(args, otherButtonTitles);
for (NSString *arg = otherButtonTitles; arg != nil; arg = va_arg(args, NSString*))
{
[self addButtonWithTitle:arg];
}
va_end(args);
}
NSLog(#"cancel button index - %d", self.cancelButtonIndex);
return self;
}
The buttons in the UIAlertView are implemented using a UITableView. That's why tapping a button triggers a tableView:didSelectRowAtIndexPath.
Typical problems causing such errors are:
Alert not displayed from the main thread
Delegate already deallocated (make sure the alert delegate is retained somewhere for the entire life of the alert).
See iOS 7 bug or my bug in UIAlertView
UIAlertView delegate is
#property(nonatomic, assign) id delegate, so ensure alertView.delegate = nil when delegate is dealloced.
I have a UITableView that is successfully populated from a database, put into a NSArray and every cell is clickable and performs the correct action when selected, however the list is out of alphabetical order. In order to sort them into alphabetical order, I use the following code:
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"Name" ascending:YES];
sortedArray = [buildings sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
The alphabetizing works correctly, however when some of the cells (in this example it was the first cell in the list) are clicked the app terminates with an uncaught exception:
'NSRangeException' '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
I'd be happy to post more code excerpts, I am just unsure of what relevant information would be needed to help answer this question.
Edit:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger r = [indexPath row];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if(infoInst != nil) {
infoInst = nil;
}
infoInst = [[DirInfoListing alloc] initWithNibName:#"DirInfoListing" bundle:nil building:[self.listData objectAtIndex:r] map:mapUI];
NSLog(#"infoInst building: %#", [self.listData objectAtIndex:r]);
NSString *deviceType = [UIDevice currentDevice].model;
if([deviceType isEqualToString:#"iPad"] || [deviceType isEqualToString:#"iPad Simulator"]){
[self presentModalViewController:infoInst animated:YES];
} else {
[self.navigationController pushViewController:infoInst animated:YES];
}
}
Edit: Here is the stack trace immediately before the error is thrown
2013-07-03 16:56:56.579 thestanforddaily[32907:1a303] Stack trace : (
0 thestanforddaily 0x002190d9 -[Directory tableView:didSelectRowAtIndexPath:] + 457
1 UIKit 0x01ae871d -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:] + 1164
2 UIKit 0x01ae8952 -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 201
3 Foundation 0x0237086d __NSFireDelayedPerform + 389
4 CoreFoundation 0x02acd966 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 22
5 CoreFoundation 0x02acd407 __CFRunLoopDoTimer + 551
6 CoreFoundation 0x02a307c0 __CFRunLoopRun + 1888
7 CoreFoundation 0x02a2fdb4 CFRunLoopRunSpecific + 212
8 CoreFoundation 0x02a2fccb CFRunLoopRunInMode + 123
9 GraphicsServices 0x0327b879 GSEventRunModal + 207
10 GraphicsServices 0x0327b93e GSEventRun + 114
11 UIKit 0x01a58a9b UIApplicationMain + 1175
12 thestanforddaily 0x00002f4d main + 141
13 thestanforddaily 0x00002e75 start + 53
)
2013-07-03 16:57:13.740 thestanforddaily[32907:1a303] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 1 beyond bounds [0 .. 0]'
*** First throw call stack:
(0x2af9052 0x28e8d0a 0x2ae5db8 0x21cb0e 0x1b1e64e 0x1b1e941 0x1b3047d 0x1b3066f 0x1b3093b 0x1b313df 0x1b31986 0x1b315a4 0x219250 0x1ae871d 0x1ae8952 0x237086d 0x2acd966 0x2acd407 0x2a307c0 0x2a2fdb4 0x2a2fccb 0x327b879 0x327b93e 0x1a58a9b 0x2f4d 0x2e75)
Thank you!
The only array access I see in didSelectRowAtIndexPath is [self.listData objectAtIndex:r]. So it would seem your listData array isn't in sync with the table's data model. From the code you posted, the array sortedArray should correspond to the data model, so make sure listData and sortedArray are kept in sync or just have one array. If you still can't figure it out, please post your UITableViewDataSource methods as well as the methods where you set up your various array variables.
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:#"Name" ascending:YES];
sortedArray = [buildings count] <= 1 ? buildings : [buildings sortedArrayUsingDescriptors:[NSArray arrayWithObject:sort]];
Unless you require sortedArray to be a different instance than buildings. Then you should change the statement accordingly.
I'm trying to assemble a toy application to interact with http servers via CoreFoundation. I'm not using NSURLConnection because I want to be able to specify a proxy different from the one set in the OSX System Preferences.
My problem is that I wasn't able to find any working example and the Apple documentation do not provide a working snippet of code.
Anyhow this is the code I pulled together:
#import <CoreFoundation/CoreFoundation.h>
#import <CoreServices/CoreServices.h>
#import <Foundation/Foundation.h>
void myCallBack (CFReadStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo){
NSLog(#"Something happened");
BOOL *done = ((BOOL *)clientCallBackInfo);
*done = TRUE;
}
int main(int argc, char *argv[]){
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CFStringRef bodyString = CFSTR(""); // Usually used for POST data
CFStringRef url = CFSTR("http://www.apple.com");
CFURLRef myURL = CFURLCreateWithString(kCFAllocatorDefault, url, NULL);
CFStringRef requestMethod = CFSTR("GET");
CFHTTPMessageRef myRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault,
requestMethod,
myURL,
kCFHTTPVersion1_1);
if (myRequest){
NSLog(#"Request created: %# %#", CFHTTPMessageCopyRequestMethod(myRequest),
CFURLGetString(CFHTTPMessageCopyRequestURL(myRequest)));
}
CFDataRef bodyData = CFStringCreateExternalRepresentation(kCFAllocatorDefault, bodyString, kCFStringEncodingASCII, 0);
CFHTTPMessageSetBody(myRequest, bodyData);
NSLog(#"HTTPMessage body set");
CFDataRef mySerializedRequest = CFHTTPMessageCopySerializedMessage(myRequest);
NSLog(#"Serialized string generated");
CFStringRef mySerializedString = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault,
mySerializedRequest,
kCFStringEncodingASCII);
NSLog(#"Serialized request: %#", mySerializedString);
CFReadStreamRef myReadStream = CFReadStreamCreateForHTTPRequest(kCFAllocatorDefault, myRequest);
BOOL done = FALSE;
CFReadStreamOpen(myReadStream);
CFOptionFlags registeredEvents = kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered;
NSLog(#"Setting read stream client");
if (CFReadStreamSetClient(myReadStream, registeredEvents, myCallBack, (void *)&done)){
CFReadStreamScheduleWithRunLoop(myReadStream, CFRunLoopGetCurrent(),
kCFRunLoopCommonModes);
while (!done){
NSLog(#"Wait");
[[NSRunLoop currentRunLoop] run];
}
}else{
NSLog(#"Error setting readstream client");
}
CFRelease(myRequest);
CFRelease(myURL);
CFRelease(url);
CFRelease(mySerializedRequest);
myRequest = NULL;
mySerializedRequest = NULL;
[pool drain];
}
The code compiles but it throws a segfault:
Reading symbols for shared libraries .+++++...................... done
2010-09-06 21:36:34.470 a.out[27973:a0f] Request created: GET http://www.apple.com
2010-09-06 21:36:34.473 a.out[27973:a0f] HTTPMessage body set
2010-09-06 21:36:34.474 a.out[27973:a0f] Serialized string generated
2010-09-06 21:36:34.474 a.out[27973:a0f] Serialized request: GET / HTTP/1.1
2010-09-06 21:36:34.478 a.out[27973:a0f] Setting read stream client
2010-09-06 21:36:34.479 a.out[27973:a0f] Wait
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: 13 at address: 0x0000000000000000
0x00007fff83b7b83d in _signalEventSync ()
(gdb) bt
#0 0x00007fff83b7b83d in _signalEventSync ()
#1 0x00007fff83b7b7f4 in _cfstream_solo_signalEventSync ()
#2 0x00007fff83b7b734 in _CFStreamSignalEvent ()
#3 0x00007fff8391c3a1 in HTTPReadStream::streamEvent ()
#4 0x00007fff83b7b883 in _signalEventSync ()
#5 0x00007fff83b7c5d9 in _cfstream_shared_signalEventSync ()
#6 0x00007fff83b1be91 in __CFRunLoopDoSources0 ()
#7 0x00007fff83b1a089 in __CFRunLoopRun ()
#8 0x00007fff83b1984f in CFRunLoopRunSpecific ()
#9 0x00007fff8300fa18 in -[NSRunLoop(NSRunLoop) runMode:beforeDate:] ()
#10 0x00007fff8300f8f7 in -[NSRunLoop(NSRunLoop) run] ()
#11 0x0000000100000c94 in main (argc=1, argv=0x7fff5fbff5b8) at test.m:56
I don't understand where the error is. Anyone can show me the light or point me to a clear example on how to manage CFHTTPStreams?
Thanks,
Andrea
The problem was that I didn't read carefully the documentation of CFReadStreamSetClient.
The third parameter has to be a reference to a CFStreamClientContext whereas I was passing a reference to an arbitrary pointer which instead has to be included into the context.
Fixing this resolved the segmentation fault and now the test ca is working as expected.
I have class called AppController which contains a mutable array of Person objects called people. Each person simply has an NSString and a float.
I also have an NSArrayController whose contentArray is bound to the people array in AppController. Then I have the usual setup of a table view bound to the array controller to show a list of all the person objects.
In my AppController class, I tried to register as an observer of the people array to add undo support. Here's how I do it:
#implementation AppController
#synthesize people;
- (id)init
{
self = [super init];
if (self != nil) {
people = [[NSMutableArray alloc] init];
undoManager = [[NSUndoManager alloc] init];
[self addObserver:self
forKeyPath:#"people"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:NULL];
}
return self;
}
- (void)undo:(id)sender
{
[undoManager undo];
}
- (void)redo:(id)sender
{
[undoManager redo];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
int kindOfChange = [[change objectForKey:NSKeyValueChangeKindKey] intValue];
int index = [[change objectForKey:NSKeyValueChangeIndexesKey] firstIndex];
Person *p;
if (kindOfChange == NSKeyValueChangeInsertion) {
p = [change objectForKey:NSKeyValueChangeNewKey];
[[undoManager prepareWithInvocationTarget:self] removePersonAtIndex:index];
} else {
p = [change objectForKey:NSKeyValueChangeOldKey];
[[undoManager prepareWithInvocationTarget:self] addPerson:p atIndex:index];
}
}
- (void)addPerson:(Person *)person atIndex:(int)index
{
[[self mutableArrayValueForKey:#"people"] addObject:person];
}
- (void)removePersonAtIndex:(int)index
{
[[self mutableArrayValueForKey:#"people"] removeObjectAtIndex:index];
}
#end
With this code, I can successfully add a person object and then undo it. I cannot, however, undo deleting a person object. I always get this exception when I undo a delete operation:
[<NSCFArray 0x3009a0> addObserver:forKeyPath:options:context:] is not supported. Key path: personName
It seems like I'm not the only one, though: http://www.cocoabuilder.com/archive/cocoa/84489-modifying-the-array-of-an-nsarraycontroller.html#84496
Any ideas?
UPDATE:
Here's the backtrace:
#0 0x7fff8161b41e in -[NSArray(NSKeyValueObserverRegistration) addObserver:forKeyPath:options:context:]
#1 0x7fff8822fc47 in -[_NSModelObservingTracker _registerOrUnregister:observerNotificationsForModelObject:]
#2 0x7fff883b2bb9 in -[_NSModelObservingTracker startObservingModelObjectAtReferenceIndex:]
#3 0x7fff883acc41 in -[_NSModelObservingTracker setObservingToModelObjectsRange:]
#4 0x7fff883aca5a in -[NSTableBinder tableView:updateVisibleRowInformation:]
#5 0x7fff883ac98e in -[_NSBindingAdaptor tableView:updateVisibleRowInformation:]
#6 0x7fff882b4c7b in -[NSTableView drawRect:]
#7 0x7fff882ab081 in -[NSView _drawRect:clip:]
#8 0x7fff882a9cf4 in -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:]
#9 0x7fff882aa05e in -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:]
#10 0x7fff882aa05e in -[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:]
#11 0x7fff882a83c6 in -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:]
#12 0x7fff882a9292 in -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:]
#13 0x7fff882a9292 in -[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:]
#14 0x7fff882a7ee8 in -[NSThemeFrame _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:]
#15 0x7fff882a479a in -[NSView _displayRectIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:]
#16 0x7fff8821dff6 in -[NSView displayIfNeeded]
#17 0x7fff88218ea2 in _handleWindowNeedsDisplay
#18 0x7fff81da9077 in __CFRunLoopDoObservers
#19 0x7fff81d84ef4 in __CFRunLoopRun
#20 0x7fff81d8484f in CFRunLoopRunSpecific
#21 0x7fff8836ab31 in _NSUnhighlightCarbonMenu
#22 0x7fff8834d0c1 in -[NSMenu performKeyEquivalent:]
#23 0x7fff8834be69 in -[NSApplication _handleKeyEquivalent:]
#24 0x7fff8821caa1 in -[NSApplication sendEvent:]
#25 0x7fff881b3922 in -[NSApplication run]
#26 0x7fff881ac5f8 in NSApplicationMain
#27 0x100001469 in main at main.m:13
What's happening is that [change objectForKey: NSKeyValueChangeNewKey] returns NSArray *, not Person *. You should call -lastObject on the array returned to get the actual Person object (assuming that the array contains only one object).