views:

68

answers:

3

I know these errors are very app-specific and almost always due to over-releasing an object. I just can't spot it and the debugging tips I have read haven't done the trick for me yet.

According to this debugging advice, my "offending object" was allocated in this code block (located in AddClass.m):

 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    if (indexPath.row == 1) {

        ClassRoster *classRoster = [[ClassRoster alloc] init];
        classRoster.managedObjectContext = managedObjectContext;
        classRoster.newClass = self.newClass;

        classRoster.title = @"Class Roster";
        [self.navigationController pushViewController:classRoster animated:YES];
        [classRoster release];

    }
}

However, I don't understand how the offending object could be from here. I believe I am appropriately releasing classRoster and nothing else is being allocated.

The error message clearly indicates that the program was trying to update an object in my classRoster TableViewController. This makes sense, because the crash occurred as I was trying to save an object in classRoster's detailViewController (AddStudent.m) that gets pushed on when an object in classRoster is selected.

To give the broader context of this problem, I am using Core Data and (as you can see from the above code) passing a single managedObjectContextbetween views in order to edit and save a list of classes and their related class rosters. I am using [managedObjectContext rollback] to cancel changes.

It might be worth mentioning that the save method inside of AddStudent.m updates the newly-created student object with strings entered by the user and then adds that student object to its parent class object via [class addStudentObject:student]. The detailViewController (AddStudent.m) is then popped with [delegate addStudent:self didFinishWithSave:YES]. Upon returning to ClassRoster.m, the managedObjectContext is saved.

If you think my Core Data management might be the problem, just let me know what code you would like to see. This is my first app and consequently I have the joy of making lots of stupid mistakes!

UPDATE:

I have honed in on the sequence of actions that results in a crash. I will always crash with the error in my question's subject line if I do the following:

  1. Select an existing class object (push on its AddClass detailViewController)
  2. Select to view the class' roster
  3. Hit the back button to return to the class object detailView
  4. Select to view the class' roster again
  5. Select to add new student or select an existing student object
  6. Choose to save

The break line is marked by "BREAK >>" in the following stack trace:

0x01e8c2f0  <+0000>  push   %ebp
0x01e8c2f1  <+0001>  mov    %esp,%ebp
0x01e8c2f3  <+0003>  push   %edi
0x01e8c2f4  <+0004>  push   %esi
0x01e8c2f5  <+0005>  push   %ebx
0x01e8c2f6  <+0006>  sub    $0x5c,%esp
0x01e8c2f9  <+0009>  call   0x1e8c2fe <___forwarding___+14>
0x01e8c2fe  <+0014>  pop    %ebx
0x01e8c2ff  <+0015>  mov    0xc(%ebp),%esi
0x01e8c302  <+0018>  test   %esi,%esi
0x01e8c304  <+0020>  je     0x1e8c620 <___forwarding___+816>
0x01e8c30a  <+0026>  mov    0x8(%ebp),%eax
0x01e8c30d  <+0029>  add    $0x4,%eax
0x01e8c310  <+0032>  mov    0x8(%ebp),%edx
0x01e8c313  <+0035>  mov    0x4(%edx),%edx
0x01e8c316  <+0038>  mov    %edx,-0x30(%ebp)
0x01e8c319  <+0041>  mov    0x4(%eax),%eax
0x01e8c31c  <+0044>  mov    %eax,-0x2c(%ebp)
0x01e8c31f  <+0047>  mov    -0x30(%ebp),%ecx
0x01e8c322  <+0050>  mov    %ecx,(%esp)
0x01e8c325  <+0053>  call   0x1f0a11e <dyld_stub_object_getClass>
0x01e8c32a  <+0058>  mov    %eax,(%esp)
0x01e8c32d  <+0061>  call   0x1f09e5a <dyld_stub_class_getName>
0x01e8c332  <+0066>  mov    %eax,-0x28(%ebp)
0x01e8c335  <+0069>  movl   $0xa,-0x34(%ebp)
0x01e8c33c  <+0076>  cld    
0x01e8c33d  <+0077>  lea    0x73a30(%ebx),%edi
0x01e8c343  <+0083>  mov    %eax,%esi
0x01e8c345  <+0085>  mov    $0xa,%ecx
0x01e8c34a  <+0090>  repz cmpsb %es:(%edi),%ds:(%esi)
0x01e8c34c  <+0092>  mov    $0x0,%eax
0x01e8c351  <+0097>  je     0x1e8c35d <___forwarding___+109>
0x01e8c353  <+0099>  movzbl -0x1(%esi),%eax
0x01e8c357  <+0103>  movzbl -0x1(%edi),%ecx
0x01e8c35b  <+0107>  sub    %ecx,%eax
0x01e8c35d  <+0109>  test   %eax,%eax
0x01e8c35f  <+0111>  jne    0x1e8c3a7 <___forwarding___+183>
0x01e8c361  <+0113>  mov    0x95d46(%ebx),%eax
0x01e8c367  <+0119>  cmpb   $0x0,(%eax)
0x01e8c36a  <+0122>  jne    0x1e8c680 <___forwarding___+912>
0x01e8c370  <+0128>  mov    -0x2c(%ebp),%edx
0x01e8c373  <+0131>  mov    %edx,(%esp)
0x01e8c376  <+0134>  call   0x1f0a214 <dyld_stub_sel_getName>
0x01e8c37b  <+0139>  mov    -0x30(%ebp),%ecx
0x01e8c37e  <+0142>  mov    %ecx,0x10(%esp)
0x01e8c382  <+0146>  mov    %eax,0xc(%esp)
0x01e8c386  <+0150>  mov    -0x28(%ebp),%eax
0x01e8c389  <+0153>  add    $0xa,%eax
0x01e8c38c  <+0156>  mov    %eax,0x8(%esp)
0x01e8c390  <+0160>  lea    0x9d822(%ebx),%eax
0x01e8c396  <+0166>  mov    %eax,0x4(%esp)
0x01e8c39a  <+0170>  movl   $0x3,(%esp)
0x01e8c3a1  <+0177>  call   0x1eb3040 <CFLog>
0x01e8c3a6  <+0182>  int3   
BREAK >> 0x01e8c3a7  <+0183>  movl   $0x11,-0x38(%ebp)
0x01e8c3ae  <+0190>  cld    
0x01e8c3af  <+0191>  lea    0x79590(%ebx),%edi
0x01e8c3b5  <+0197>  mov    -0x28(%ebp),%esi
0x01e8c3b8  <+0200>  mov    $0x11,%ecx
0x01e8c3bd  <+0205>  repz cmpsb %es:(%edi),%ds:(%esi)
0x01e8c3bf  <+0207>  mov    $0x0,%eax
0x01e8c3c4  <+0212>  je     0x1e8c3d0 <___forwarding___+224>
0x01e8c3c6  <+0214>  movzbl -0x1(%esi),%eax
0x01e8c3ca  <+0218>  movzbl -0x1(%edi),%ecx
0x01e8c3ce  <+0222>  sub    %ecx,%eax
0x01e8c3d0  <+0224>  mov    -0x30(%ebp),%edx
0x01e8c3d3  <+0227>  mov    %edx,-0x24(%ebp)
0x01e8c3d6  <+0230>  test   %eax,%eax
0x01e8c3d8  <+0232>  jne    0x1e8c3e0 <___forwarding___+240>
0x01e8c3da  <+0234>  mov    0x4(%edx),%ecx
0x01e8c3dd  <+0237>  mov    %ecx,-0x24(%ebp)
0x01e8c3e0  <+0240>  mov    0xa1dbe(%ebx),%esi
0x01e8c3e6  <+0246>  mov    -0x24(%ebp),%eax
0x01e8c3e9  <+0249>  mov    %eax,(%esp)
0x01e8c3ec  <+0252>  call   0x1f0a11e <dyld_stub_object_getClass>
0x01e8c3f1  <+0257>  mov    %esi,0x4(%esp)
0x01e8c3f5  <+0261>  mov    %eax,(%esp)
0x01e8c3f8  <+0264>  call   0x1f09e72 <dyld_stub_class_respondsToSelector>
0x01e8c3fd  <+0269>  test   %al,%al
0x01e8c3ff  <+0271>  je     0x1e8c580 <___forwarding___+656>
0x01e8c405  <+0277>  mov    -0x2c(%ebp),%edx
0x01e8c408  <+0280>  mov    %edx,0x8(%esp)
0x01e8c40c  <+0284>  mov    %esi,0x4(%esp)
0x01e8c410  <+0288>  mov    -0x24(%ebp),%ecx
0x01e8c413  <+0291>  mov    %ecx,(%esp)
0x01e8c416  <+0294>  call   0x1f0a0ee <dyld_stub_objc_msgSend>
0x01e8c41b  <+0299>  mov    %eax,-0x20(%ebp)
0x01e8c41e  <+0302>  mov    %eax,-0x1c(%ebp)
0x01e8c421  <+0305>  test   %eax,%eax
0x01e8c423  <+0307>  je     0x1e8c5ac <___forwarding___+700>
0x01e8c429  <+0313>  mov    0xa1df6(%ebx),%eax
0x01e8c42f  <+0319>  mov    %eax,0x4(%esp)
0x01e8c433  <+0323>  mov    -0x1c(%ebp),%edx
0x01e8c436  <+0326>  mov    %edx,(%esp)
0x01e8c439  <+0329>  call   0x1f0a0ee <dyld_stub_objc_msgSend>
0x01e8c43e  <+0334>  mov    (%eax),%edx
0x01e8c440  <+0336>  mov    0x18(%edx),%eax
0x01e8c443  <+0339>  shr    $0x16,%eax
0x01e8c446  <+0342>  and    $0x1,%eax
0x01e8c449  <+0345>  cmp    0xc(%ebp),%eax
0x01e8c44c  <+0348>  je     0x1e8c498 <___forwarding___+424>
0x01e8c44e  <+0350>  lea    0x6eaee(%ebx),%eax
0x01e8c454  <+0356>  lea    0x7979f(%ebx),%esi
0x01e8c45a  <+0362>  mov    0xc(%ebp),%ecx
0x01e8c45d  <+0365>  test   %ecx,%ecx
0x01e8c45f  <+0367>  mov    %eax,%edi
0x01e8c461  <+0369>  cmove  %esi,%edi
0x01e8c464  <+0372>  testb  $0x40,0x1a(%edx)
0x01e8c468  <+0376>  cmovne %eax,%esi
0x01e8c46b  <+0379>  mov    -0x2c(%ebp),%ecx
0x01e8c46e  <+0382>  mov    %ecx,(%esp)
0x01e8c471  <+0385>  call   0x1f0a214 <dyld_stub_sel_getName>
0x01e8c476  <+0390>  mov    %edi,0x10(%esp)
0x01e8c47a  <+0394>  mov    %esi,0xc(%esp)
0x01e8c47e  <+0398>  mov    %eax,0x8(%esp)
0x01e8c482  <+0402>  lea    0x9d852(%ebx),%eax
0x01e8c488  <+0408>  mov    %eax,0x4(%esp)
0x01e8c48c  <+0412>  movl   $0x4,(%esp)
0x01e8c493  <+0419>  call   0x1eb3040 <CFLog>
0x01e8c498  <+0424>  mov    -0x20(%ebp),%eax
0x01e8c49b  <+0427>  mov    %eax,0x8(%esp)
0x01e8c49f  <+0431>  mov    0xa1dba(%ebx),%eax
0x01e8c4a5  <+0437>  mov    %eax,0x4(%esp)
0x01e8c4a9  <+0441>  mov    0xa1e76(%ebx),%eax
0x01e8c4af  <+0447>  mov    %eax,(%esp)
0x01e8c4b2  <+0450>  call   0x1f0a0ee <dyld_stub_objc_msgSend>
0x01e8c4b7  <+0455>  mov    %eax,%edi
0x01e8c4b9  <+0457>  mov    0xa1dde(%ebx),%eax
A: 

I don't think you have over-released the instance.

To me, it looks like the deallocated instance is of some other class but receives a message from ClassRoaster.

Can you post the error message and the stack trace from the moment you app crashes?

Codo
My question subject is the full error as shown in the console. I am unfamiliar with using the stack trace. Is that the massive list of addresses if I open Run -> Debugger?
Spindler
A: 

Here's what's more likely:

  • Something does instanceVariable = [NSArray arrayWithObjects:..., nil];
  • The NSArray was not retained, so it gets released at the end of the run loop.
  • Something taps the row. The NSArray's memory gets reused to make your ClassRoster object.
  • You navigate out of ClassRoster, and it gets released and dealloced.
  • Something tries to access instanceVariable. It points to the memory that used to be a NSArray, but has now been overwritten by ClassRoster.

Try setting the environment variable NSZombieEnabled=YES (in Project -> Edit Active Executable). You can also set NSDeallocateZombies=NO, but AIUI that's the default.

Turn it off once you're finished debugging.

EDIT: Oops (I should've realized you had zombies on, and looked at the method name).

If you run it in the debugger (you might need breakpoints active), it should stop at the thing that called controllerWillChangeContent:. Perhaps your ClassRoster is being set as the delegate for something, and is getting a delegate callback after it's been dealloced? I always set the relevant delegates to nil in dealloc to prevent problems like this.

tc.
I already had NSZombieEnabled=YES. Should that automatically provide more info in the console on crash, or do I need to enter a command to check for zombie objects?
Spindler
Hmmm...yeah, it is a delegate for the detailView that gets pushed onto it so that it can receive notifications on whether the user chose to save or not. Would you recommend scrapping that approach or is there an "code-appropriate" way I can keep ClassRoster from being deallocated? P.S. I updated my post.
Spindler
In dealloc, `detailView.delegate = nil` (but shouldn't the detail view have been dealloced first?).
tc.
Inside of the two view controllers that I set as delegates for their subsequent detailViewControllers, I added `detailView.delegate = nil` to `-dealloc`. I then got this error after hitting the back button on a one of the delegate view controllers: `-[AddStudent setDelegate:]: message sent to deallocated instance 0x458ea20`
Spindler
Of course, you need to do `detailView.delegate = nil` before `[detailView release]` (I'm assuming detailView is an AddStudent* at that point).
tc.
A: 

'Illegal attempt to establish a relationship 'myClass' between objects in different contexts'

That line describes the issue. You are probably using code from an Apple example that has you building more than one NSManagedObjectContext correct? Perhaps the code suggested to create a second one so that you can easily cancel out of an edit or some such?

That is your problem. You are trying to connect two objects that are in two different NSManagedObjectContext instances. Consider each NSManagedObjectContext to be a sandbox and you can't mix them.

However, you do not need to have more than one NSManagedObjectContext. The example that does that is a very bad example. A single NSManagedObjectContext is more than sufficient for any single threaded application. Remove the second NSManagedObjectContext from your application and this issue will go away.

Update

I found the error. You cannot create a NSManagedObject with -init. That is not the designated initializer for an NSManagedObject. You must either create it with

-initWithEntity: insertIntoManagedObjectContext:

or use the class method

+[NSEntityDescription insertNewObjectForEntityForName: inManagedObjectContext]

Those are the only two valid ways to create a new NSManagedObject.]

Update

I strongly suggest you use better naming conventions, ClassRosterViewController is far more descriptive. ClassRoster really sounds like data object.

My original assumption still stands, somewhere, somehow you are trying to connect two NSManagedObject instances that do not belong to the same NSManagedObjectContext. Either you have two instances of NSManagedObjectContext or you are creating a NSManagedObject without an associated NSManagedObjectContext.

However, my responses are based around the error message that is in your question. Where are you seeing the error that is in your title. Can you paste that stack trace as well?

Marcus S. Zarra
I only have a single `NSManagedObjectContext` (see my 3rd paragraph after 1st code block) which I pass first to my `RootViewController` and then on to each subsequent controller (though passing the entire context to each of the aforementioned views is perhaps overkill). I AM still using the delegate notifications used in the Apple CoreDataBooks example (i.e. `-didFinishWithSave`), but I am only using one MOC.
Spindler
If you are referring to my first block of code, no `NSManagedObject` is being initialized there. `ClassRoster` is a `UITableViewController` which I am pushing onto to the stack. I have double-checked my `NSManagedObject` initializations elsewhere, however, and they all match the two initializers you listed above.
Spindler
Stack trace now listed in update. I apologize for my naming conventions. I am changing them right now.
Spindler