ios - UITableView delete/add row causes CoreData: Serious Application Error if another object has been selected in the MasterView of a SplitViewController -


update 18/3 #2. i've started counting beginupdates , endupdates make sure they're even. right before there's exception, out of sync. not sure why though.

update 18/3: think i've found problem, i'm not sure if know how fix it. after experimenting couple hours, found crash app when had selected more 1 item in master tableview of svc during session. when item selected in master tableview, detail table view gets new object set , refreshtables called if it's halfway through update/move/delete/insert cycle. hence problem; think has instructions update old story object though new story object has been set on detailviewcontroller.

how can animation/coredata/tableview updates processed before new story set in detail view? save changes managedobjectcontext when seteditting == no. i'm guessing need make custom setstory setter processes updates uitableview/coredata set before accepting new object?

this code gets called on master tableview controller of svc in didselectrowatindexpath:

[detailviewcontroller setstory:storyset]; //where storyset new story object [detailviewcontroller refreshtables]; 

i have intermittent errors on attempting delete row action won't animate , application hangs following error (the row deleted coredata set though). happens if have chosen more 1 row master tableview controller in svc in 1 session.

after googling thought might problem (void)controller:(nsfetchedresultscontroller *)controller didchangeobject:(id) called after update animate changes user has made.

how can fix these intermittent bugs?

  • 16/3i've tried simplify code. i've removed calls managed object context , put them in setediting, removed superfluous [self.tableview reloaddata] , [self.tableview setneedsdisplay] , invalidated 'reordering' bool (it's still in code, it's set no, makes no difference). uitableview more stable ever, still manage intermittent errors on delete (and on add) - seems take while crash still will.**
  • 15/3: think has coredata / uitableview being out of sync - coredata thinks has less or more uitableview
  • there's no problem coredata set, it's animation/ui side of things (things removed)
  • it's intermittent, on deletes
  • after railwayparade implemented nsfetchedresultschangemove in didchangeobject: fixed moving error not delete errors.

can see i've missed or can check? happy give more information if helps solve problem.

apologies obscene amount of code posted here.

the error:

coredata: error: serious application error. exception caught delegate of nsfetchedresultscontroller during call -controllerdidchangecontent:. attempt insert row 3 section 0, there 3 rows in section 0 after update userinfo (null)

// //  makesentencetableviewcontroller.h //  storybot // //  created glen storey on 25/10/10. //  copyright 2010 glen storey. rights reserved. //  #import <uikit/uikit.h> #import "addstoryitem.h" #import "story.h" #import "sentence.h"   @interface makesentencetableviewcontroller : uitableviewcontroller <nsfetchedresultscontrollerdelegate, addstoryitemdelegate, uinavigationcontrollerdelegate, uiimagepickercontrollerdelegate, uitextfielddelegate, uipopovercontrollerdelegate> {     nsmanagedobjectcontext      *managedobjectcontext;       nsfetchedresultscontroller  *fetchedresultscontroller;     uipopovercontroller         *popovercontroller;     uibarbuttonitem             *playbuttonitem;     uibarbuttonitem             *addbuttonitem;      bool                        reordering;     bool                        insert;     bool                        delete;     bool                        move;      int                         beginupdatescount;     int                         endupdatescount;  } @property (nonatomic, retain)   story *story; @property (nonatomic, retain)   nsmanagedobjectcontext *managedobjectcontext; @property (nonatomic, retain)   uibarbuttonitem *playbuttonitem; @property (nonatomic, retain)   uibarbuttonitem *addbuttonitem; @property bool reordering,  insert,  delete,  move;  -(ibaction)createstorymodal:(id)sender; -(void)refreshtables; -(ibaction)pushshare:(id)sender;   @end   // //  makesentencetableviewcontroller.m //   // //  created glen storey on 25/10/10. //  copyright 2010 glen storey. rights reserved. //  #import "makesentencetableviewcontroller.h" #import "shareviewcontroller.h" #import "storybotappdelegate.h"  @implementation makesentencetableviewcontroller @synthesize story, managedobjectcontext, addbuttonitem, playbuttonitem, reordering, insert, delete, move;  -(void)addstoryitemaction:(nsstring*)text order:(nsnumber*)order image:(nsstring*)image thumb:(nsstring*)thumb{      nslog(@"text: %@, order: %@, image: %@.", text, order, image);     nslog(@"beginupdatescount: %d vs. endupdatescount: %d", beginupdatescount, endupdatescount);      nsset *sentences = [story sentences];     nsnumber *maxorder = [sentences valueforkeypath:@"@max.order"];     nslog(@"maxorder: %@", maxorder);      if(maxorder == 0){          maxorder = [[nsnumber alloc] initwithinteger: 0];     }      //make new sentence!     sentence *sentence = [nsentitydescription insertnewobjectforentityforname:@"sentence"                                                         inmanagedobjectcontext:managedobjectcontext];      [sentence settext: text];     [sentence setimage: image];     [sentence setthumb: thumb];     [sentence setbelongsto: story];     if([maxorder intvalue] >= 1 ){             [sentence setorder: [[nsnumber alloc] initwithinteger:[maxorder intvalue]+1]];      }else{             [sentence setorder: [[nsnumber alloc] initwithinteger:1]];     }     nsmutableset *mutableset = [[nsmutableset alloc] initwithset:sentences];     [mutableset addobject:sentence];      //nslog(@"sentences before setwithset %@", mutableset);      sentences = [[nsset alloc] initwithset: mutableset];      //nslog(@"sentences after setwithset %@", sentences);       [story setsentences:sentences];      //nserror *error;        //bool issaved = [managedobjectcontext save:&error];     //nslog(@"issaved? %@", (issaved ? @"yes" :@"no ") );      //if (!issaved) {         //nslog(@"%@:%s error saving context: %@", [self class], _cmd, [error localizeddescription]);         //don't worry warning - rem out when finished (just log)       //  return;     //}       [sentences release];     [mutableset release];      //[self.tableview reloaddata];     //[self.tableview setneedsdisplay];     [self dismissmodalviewcontrolleranimated:yes];      }  #pragma mark - #pragma mark view lifecycle  -(id)initwithnibname:(nsstring*)name bundle:(nsbundle*)bundle; {     self = [super initwithnibname:name bundle:bundle];      if (self) {          self.title = @"my stories";          addbuttonitem = [[uibarbuttonitem alloc] initwithbarbuttonsystemitem:uibarbuttonsystemitemadd                                                                                         target:self                                                                                        action:@selector(createstorymodal:)];          playbuttonitem = [[uibarbuttonitem alloc] initwithbarbuttonsystemitem:uibarbuttonsystemitemplay                                                                                          target:self                                                                                         action:@selector(pushshare:)];         if (ui_user_interface_idiom() == uiuserinterfaceidiompad) {              [addbuttonitem setenabled:no];             [playbuttonitem setenabled:no];         }           nsarray* toolbaritems = [nsarray arraywithobjects:                                  addbuttonitem,                                  [[uibarbuttonitem alloc] initwithbarbuttonsystemitem:uibarbuttonsystemitemflexiblespace                                                                                 target:nil                                                                                action:nil],                                  playbuttonitem,                                  nil];          [toolbaritems makeobjectsperformselector:@selector(release)];         self.toolbaritems = toolbaritems;            //nslog(@"self: %@ self.toolbaritems: %@", self, self.toolbaritems);           //do need release uibarbuttonitems?           nslog(@"initwithnibname:");     }        return self; }  -(void)refreshtables{      //use refresh new table content. calls self.tableview reloaddata don't need call afterward.       nslog(@"===================================refreshtables");     nslog(@"story object %@", story);      if (managedobjectcontext == nil)      {          nslog(@"managedobjectcontext == nil");         managedobjectcontext = [(storybotappdelegate *)[[uiapplication sharedapplication] delegate] managedobjectcontext];         nslog(@"after managedobjectcontext: %@",  managedobjectcontext);     }else{         nslog(@"managedobjectcontext != nil");     }        nsfetchrequest *request = [[nsfetchrequest alloc] init];     nsentitydescription *entity = [nsentitydescription entityforname:@"sentence" inmanagedobjectcontext:managedobjectcontext];     [request setentity:entity];      //sorting stuff:     nssortdescriptor *sortdescriptor = [[nssortdescriptor alloc] initwithkey:@"order" ascending: yes];     nsarray *sortdescriptors = [[nsarray alloc] initwithobjects: sortdescriptor, nil];     [request setsortdescriptors:sortdescriptors];     [sortdescriptors release];     [sortdescriptor release];      nspredicate *predicatetitle = [nspredicate predicatewithformat:@"belongsto=%@",story];     [request setpredicate :predicatetitle];      nsdateformatter *dateformatter = [[[nsdateformatter alloc] init] autorelease];     [dateformatter setdateformat:@"eee, d mmm yyyy hh:mm:ss"];     nsstring *datestring = [dateformatter stringfromdate:[story creationdate]];      nslog(@"datestring: %@", datestring);      fetchedresultscontroller = [[nsfetchedresultscontroller alloc]                                  initwithfetchrequest:request managedobjectcontext:managedobjectcontext                                  sectionnamekeypath:nil cachename:datestring];      fetchedresultscontroller.delegate = self;      [request release];      nserror *error;     [fetchedresultscontroller performfetch:&error];      [self.tableview reloaddata];  }  - (void)viewdidload {     [super viewdidload];      self.title = @"my story";      nslog(@"passed story object: %@", story);     //nslog(@"managedobjectcontext: %@", managedobjectcontext);      //need predicate belongsto here.      self.tableview.rowheight = 50;      if(story != null){         if (managedobjectcontext == nil)          {              nslog(@"managedobjectcontext == nil");             managedobjectcontext = [(storybotappdelegate *)[[uiapplication sharedapplication] delegate] managedobjectcontext];             nslog(@"after managedobjectcontext: %@",  managedobjectcontext);         }else{             nslog(@"managedobjectcontext != nil");         }            nsfetchrequest *request = [[nsfetchrequest alloc] init];         nsentitydescription *entity = [nsentitydescription entityforname:@"sentence" inmanagedobjectcontext:managedobjectcontext];         [request setentity:entity];          //sorting stuff:         nssortdescriptor *sortdescriptor = [[nssortdescriptor alloc] initwithkey:@"order" ascending: yes];         nsarray *sortdescriptors = [[nsarray alloc] initwithobjects: sortdescriptor, nil];         [request setsortdescriptors:sortdescriptors];         [sortdescriptors release];         [sortdescriptor release];          nspredicate *predicatetitle = [nspredicate predicatewithformat:@"belongsto=%@",story];         [request setpredicate :predicatetitle];            fetchedresultscontroller = [[nsfetchedresultscontroller alloc]                                      initwithfetchrequest:request managedobjectcontext:managedobjectcontext                                      sectionnamekeypath:nil cachename:nil];          fetchedresultscontroller.delegate = self;          [request release];          nserror *error;         [fetchedresultscontroller performfetch:&error];         }    #pragma mark - #pragma mark table view data source  - (nsinteger)numberofsectionsintableview:(uitableview *)tableview {     // return number of sections.     return 1; }   - (nsinteger)tableview:(uitableview *)tableview numberofrowsinsection:(nsinteger)section {      //return number of rows in section.     nsarray *sections = [fetchedresultscontroller sections];     nsinteger count = 0;      if ([sections count]){         id <nsfetchedresultssectioninfo> sectioninfo = [sections objectatindex:section];         count = [sectioninfo numberofobjects];      }     nslog(@"# of rows in section %d", count);     return count;  }   // customize appearance of table view cells. - (uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath {      static nsstring *cellidentifier = @"cell";      uitableviewcell *cell = [tableview dequeuereusablecellwithidentifier:cellidentifier];     if (cell == nil) {         cell = [[[uitableviewcell alloc] initwithstyle:uitableviewcellstyledefault reuseidentifier:cellidentifier] autorelease];     }      // configure cell...      sentence *sentenceatcell = [fetchedresultscontroller objectatindexpath:indexpath];     //nslog(@"sentenceatcell: %@", sentenceatcell);      cell.textlabel.text = [sentenceatcell text];        nsarray *paths       = nssearchpathfordirectoriesindomains(nsdocumentdirectory, nsuserdomainmask, yes);      nsstring *uniquepath = [[paths objectatindex:0] stringbyappendingpathcomponent:[sentenceatcell thumb]];       // should crop want - you've got create croprect.      uiimage *largeimage = [uiimage imagewithcontentsoffile: uniquepath];     cgrect croprect = cgrectmake(0, 0, 66, 50);        /*if ([[uiscreen mainscreen] respondstoselector:@selector(scale)]) {         if ([[uiscreen mainscreen] scale] == 2) {             // running iphone 4 or equiv. res device.             croprect = cgrectmake(15, 14, 100, 75);          }     }*/       cgimageref imageref = cgimagecreatewithimageinrect([largeimage cgimage], croprect);     cell.imageview.image = [uiimage imagewithcgimage: imageref];     cgimagerelease(imageref);        //nslog(@"indexpath: %@", indexpath);      return cell; }  // override support editing table view. - (void)tableview:(uitableview *)tableview commiteditingstyle:(uitableviewcelleditingstyle)editingstyle forrowatindexpath:(nsindexpath *)indexpath {     if (editingstyle == uitableviewcelleditingstyledelete) {          nslog(@"delete row");         nsfilemanager *filemanager = [nsfilemanager defaultmanager];         nsarray *paths = nssearchpathfordirectoriesindomains(nsdocumentdirectory, nsuserdomainmask, yes);          nsstring *documentsdirectorypath = [paths objectatindex:0];           //  1. @ sentence we're delete.         sentence *sentenceretire =      [fetchedresultscontroller objectatindexpath:indexpath];         //  2. have order of 0?          nslog(@"=================== sentenceretire: %@", sentenceretire);           if([[sentenceretire order] intvalue] == 0){                 //      yes: there sentence in story?                 story *storyretire                      = [sentenceretire belongsto];                 nsset *sentencesinretiredsentenceset    = [storyretire sentences];                   if ([sentencesinretiredsentenceset count] > 1){                     //          yes:    set sentence smallest order order of 0                        //                  delete sentence + files                      nspredicate *predicatetitle             = [nspredicate predicatewithformat:@"order>0"];                     nsset *sentenceswithpotentialtobetitle  = [sentencesinretiredsentenceset filteredsetusingpredicate:predicatetitle];                     nsnumber *minorder                      = [sentenceswithpotentialtobetitle valueforkeypath:@"@min.order"];                        nspredicate *predicateorder             = [nspredicate predicatewithformat:@"order=%d",[minorder intvalue]];                     nsset *sentencewithpotentialtobetitle   = [sentenceswithpotentialtobetitle filteredsetusingpredicate:predicateorder];                         //note sentence (singular) not sentences. sentence who's order needs updated.                          nslog(@"setencewithpotentialtobetitle (check order isn't null on crash): %@", sentencewithpotentialtobetitle);                       sentence *sentencetopromote = [sentencewithpotentialtobetitle anyobject];                      //now know sentence promote can delete sentence & files.                          [managedobjectcontext deleteobject:[fetchedresultscontroller objectatindexpath:indexpath]];                      nsstring *imagetrash = [documentsdirectorypath stringbyappendingpathcomponent:(nsstring*)[sentenceretire image]];                     nsstring *thumbtrash = [documentsdirectorypath stringbyappendingpathcomponent:[sentenceretire thumb]];                      nslog(@"about delete these files: %@, %@", imagetrash, thumbtrash);                      [filemanager removeitematpath:imagetrash error:null];                     [filemanager removeitematpath:thumbtrash error:null];                      //and promote new title.                         [sentencetopromote setorder:[[nsnumber alloc] initwithinteger:0]];                    }else{                     //          no:     we're deleting story                     //                  delete files!                      [managedobjectcontext deleteobject:storyretire];                      nsstring *imagetrash = [documentsdirectorypath stringbyappendingpathcomponent:(nsstring*)[sentenceretire image]];                     nsstring *thumbtrash = [documentsdirectorypath stringbyappendingpathcomponent:[sentenceretire thumb]];                      //nslog(@"about delete these files: %@, %@", imagetrash, thumbtrash);                      [filemanager removeitematpath:imagetrash error:null];                     [filemanager removeitematpath:thumbtrash error:null];                  //pop back.                      if (ui_user_interface_idiom() == uiuserinterfaceidiompad) {                               nslog(@"last sentence in story - delete story , point, somewhere!");                         //probably delete sentece clear table, delete story using storyretire                      } else{                         [self.navigationcontroller popviewcontrolleranimated:yes];                     }             }          }else{         //  no: delete sentence object.              [managedobjectcontext deleteobject:[fetchedresultscontroller objectatindexpath:indexpath]];              nsstring *imagetrash = [documentsdirectorypath stringbyappendingpathcomponent:(nsstring*)[sentenceretire image]];             nsstring *thumbtrash = [documentsdirectorypath stringbyappendingpathcomponent:[sentenceretire thumb]];              //nslog(@"about delete these files: %@, %@", imagetrash, thumbtrash);              [filemanager removeitematpath:imagetrash error:null];             [filemanager removeitematpath:thumbtrash error:null];          }           // save context.         //nserror *error;         //if (![managedobjectcontext save:&error]) {             /*              replace implementation code handle error appropriately.               abort() causes application generate crash log , terminate. should not use function in shipping application, although may useful during development. if not possible recover error, display alert panel instructs user quit application pressing home button.              */         //  nslog(@"unresolved error %@, %@", error, [error userinfo]);         //  abort();         //}      }  }   - (void)setediting:(bool)editing animated:(bool)animated {     [super setediting:editing animated:animated];     if (!editing) {         //save here         nserror *error;         bool issaved = [managedobjectcontext save:&error];         nslog(@"issaved? %@ ======================================", (issaved ? @"yes" :@"no ") );     } }   // override support rearranging table view. - (void)tableview:(uitableview *)tableview moverowatindexpath:(nsindexpath *)fromindexpath toindexpath:(nsindexpath *)toindexpath {     //this implementation here: http://www.cimgf.com/2010/06/05/re-ordering-nsfetchedresultscontroller/      nsmutablearray *things = [[fetchedresultscontroller fetchedobjects] mutablecopy];      // grab item we're moving.     nsmanagedobject *thing = [fetchedresultscontroller objectatindexpath:fromindexpath];      // remove object we're moving array.     [things removeobject:thing];     // re-insert @ destination.     [things insertobject:thing atindex:[toindexpath row]];      // of objects in correct order. update each     // object's displayorder field iterating through array.       int = 0;     (nsmanagedobject *mo in things)     {         [mo setvalue:[nsnumber numberwithint:i++] forkey:@"order"];     }       nslog(@"things: %@", things);     [things release], things = nil;       //reordering = yes;     //nslog(@"moverowatindexpath: reordering");     //nserror *error;       //[managedobjectcontext save:&error];        }    #pragma mark - #pragma mark table view delegate /**  delegate methods of nsfetchedresultscontroller respond additions, removals , on.  */  - (void)controllerwillchangecontent:(nsfetchedresultscontroller *)controller {      // fetch controller start sending change notifications, prepare table view updates.     nslog(@"controllerwillchangecontent: beginupdates");      [self.tableview beginupdates];      beginupdatescount++;     nslog(@"====================beginupdates incremented");     nslog(@"beginupdatescount %d", beginupdatescount);     nslog(@"endupdatescount   %d", endupdatescount);  }   - (void)controller:(nsfetchedresultscontroller *)controller didchangeobject:(id)anobject atindexpath:(nsindexpath *)indexpath forchangetype:(nsfetchedresultschangetype)type newindexpath:(nsindexpath *)newindexpath {      nslog(@"didchangeobject: %@", anobject);      uitableview *tableview;     tableview = self.tableview;      switch(type) {         case nsfetchedresultschangeinsert:             nslog(@"resultschangeinsert:");             insert = yes;             [tableview insertrowsatindexpaths:[nsarray arraywithobject:newindexpath] withrowanimation:uitableviewrowanimationfade];              break;          case nsfetchedresultschangedelete:             nslog(@"resultschangedelete:");             delete = yes;             [tableview deleterowsatindexpaths:[nsarray arraywithobject:indexpath] withrowanimation:uitableviewrowanimationfade];              break;          case nsfetchedresultschangemove:             nslog(@"resultschangemove:");             move = yes;             [tableview deleterowsatindexpaths:[nsarray arraywithobject:indexpath] withrowanimation:uitableviewrowanimationnone];             [tableview insertrowsatindexpaths:[nsarray arraywithobject:newindexpath] withrowanimation:uitableviewrowanimationnone];             break;          default:             nslog(@"switch problem - default");     }  }  - (void)controller:(nsfetchedresultscontroller *)controller didchangesection:(id <nsfetchedresultssectioninfo>)sectioninfo atindex:(nsuinteger)sectionindex forchangetype:(nsfetchedresultschangetype)type {      nslog(@"didchangesection:");     switch(type) {         case nsfetchedresultschangeinsert:             [self.tableview insertsections:[nsindexset indexsetwithindex:sectionindex] withrowanimation:uitableviewrowanimationfade];             break;          case nsfetchedresultschangedelete:             [self.tableview deletesections:[nsindexset indexsetwithindex:sectionindex] withrowanimation:uitableviewrowanimationfade];             break;          } }   - (void)controllerdidchangecontent:(nsfetchedresultscontroller *)controller {      nslog(@"didchangecontent");     // fetch controller has sent current change notifications, tell table view process updates.     nslog(@"almost endupdates==============================================");     if(delete){         nslog(@"endupdates delete");         delete = no;     }     if(move){         nslog(@"endupdates move");         move = no;     }     if(insert){         nslog(@"endupdates insert");         insert = no;     }      [self.tableview endupdates];     endupdatescount++;      nslog(@"====================endupdates incremented");     nslog(@"endupdatescount   %d", endupdatescount);     nslog(@"beginupdatescount %d", beginupdatescount);      nslog(@"endupdates finished");  }   #pragma mark - #pragma mark memory management  - (void)dealloc {     nslog(@"dealloc sentence");     //[fliteengine stoptalking];     //[fliteengine release];     addbuttonitem   = nil;     playbuttonitem  = nil;      if (ui_user_interface_idiom() == uiuserinterfaceidiompad) {          //it doesn't seem allocated because it's not set on iphone.          [addbuttonitem  release];         [playbuttonitem release];     }      [story release];     [fetchedresultscontroller release];      [super dealloc]; }   @end 

update 18/3 #3 read this question fetchedresultscontroller needing have delegate set nil before creating new one. put

detailviewcontroller.fetchedresultscontroller = nil; detailviewcontroller.fetchedresultscontroller.delegate = nil; 

in svc master view on didselectrowatindexpath , problem has stopped. refreshtables created new fetchedresultscontroller every time row selected in master view, , think still interfering when new story object selected.


Comments

Popular posts from this blog

jasper reports - Fixed header in Excel using JasperReports -

media player - Android: mediaplayer went away with unhandled events -

python - ('The SQL contains 0 parameter markers, but 50 parameters were supplied', 'HY000') or TypeError: 'tuple' object is not callable -