2011-08-28 166 views
1

我有一些代碼將課程下載爲JSON,解析它們並將它們放入核心數據。然後它們顯示在UITableView中。目前,當用戶有很多課程時,連接有時會超時。所以我試圖解析這些課程(使用SBJson)並將它們一次添加到tableview中。核心數據NSObjectInaccessibleException NSManagedObject已失效

兩個代碼基本上是相同的,但新的代碼產生時的tableView的東西踢崩潰,並顯示錯誤

"Terminating app due to uncaught exception 
'NSObjectInaccessibleException', reason: 'The NSManagedObject with ID:0x5ad0570 <x-coredata://A21AC71F-175B-423D-BF7D-C67BEE094460/Lessons/p18> has been invalidated.'" 

我想知道有什麼區別這兩者之間代碼清單可能會導致此錯誤。原始代碼在循環中創建每個核心數據對象,但新代碼在下載時創建每個核心數據對象。 listViewArray是用來填充UITableView的數組。

我使用SBJsonStreamParser和SBJsonStreamParserAdapter解析JSON,因爲它的用武之地。

我有一個工作的實施(未顯示),基本上調用每一個接收到新的對象時,下面的原代碼(運行通過每次接收到的對象的完整循環)。不過,我想知道是什麼導致了錯誤,而不僅僅是讓事情起作用。

這是原來的非流碼,堪稱connectionDidFinishLoading:

NSMutableArray *tempListArray = [[NSMutableArray alloc] initWithArray:jsonStreamedData]; 

    if (listViewArray) 
     [listViewArray release]; 
    listViewArray = [[NSMutableArray alloc] init]; 

    if(![tempListArray count]){ 
     UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Message" message:@"No active lessons " delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; 
     [alertView show]; 
     [alertView release]; 
    } 
    else { 
     MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
     NSError *error = nil; 
     [appDelegate.managedObjectContext reset]; 

     NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext; 
     for (int i = 0; i < [tempListArray count]; i++) { 

      NSFetchRequest *checkRequest = [[NSFetchRequest alloc] init]; 
      NSEntityDescription *lessonEntity = [NSEntityDescription entityForName:@"Lessons" inManagedObjectContext:managedObjectContext]; 
      [checkRequest setEntity:lessonEntity]; 
      NSPredicate *langPredicate = [NSPredicate predicateWithFormat:@"(language = %@)", appDelegate.currentLanguage]; 
      NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"(username = %@)", appDelegate.userName]; 
      NSPredicate *idPredicate = [NSPredicate predicateWithFormat:@"(content_Id = %@)", [[tempListArray objectAtIndex:i] valueForKey:@"id"]]; 
      [checkRequest setPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:langPredicate, userPredicate, idPredicate, nil]]]; 

      NSArray *checkResults = [managedObjectContext executeFetchRequest:checkRequest error:&error]; 
      [checkRequest release]; 

      NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init]; 
      if ([checkResults count]) { 
       Lessons *lessonObj = [checkResults objectAtIndex:0]; 
       lessonObj.cards_count = [[tempListArray objectAtIndex:i] valueForKey:@"cards_count"]; 
       lessonObj.mTitle = [[tempListArray objectAtIndex:i] valueForKey:@"title"]; 
       lessonObj.sound_Url = [[tempListArray objectAtIndex:i] valueForKey:@"audio_url"]; 
       lessonObj.mId = [NSNumber numberWithInt:i]; 
       [tempDict setValue:lessonObj forKey:@"lesson"]; 
       [tempDict setValue: [[tempListArray objectAtIndex:i] objectForKey:@"image_url"] forKey:@"image_url"]; 
       [listViewArray addObject:tempDict]; 

      } 
      else { 

       Lessons *newLesson = (Lessons *)[NSEntityDescription insertNewObjectForEntityForName:@"Lessons" inManagedObjectContext:appDelegate.managedObjectContext]; 
       newLesson.cards_count = [[tempListArray objectAtIndex:i] valueForKey:@"cards_count"]; 
       newLesson.mTitle = [[tempListArray objectAtIndex:i] valueForKey:@"title"]; 
       newLesson.sound_Url = [[tempListArray objectAtIndex:i] valueForKey:@"audio_url"]; 
       newLesson.content_Id = [[tempListArray objectAtIndex:i] valueForKey:@"id"]; 
       newLesson.username = appDelegate.userName; 
       newLesson.language = appDelegate.currentLanguage; 
       newLesson.mId = [NSNumber numberWithInt:i]; 
       [tempDict setValue:newLesson forKey:@"lesson"]; 
       [tempDict setValue: [[tempListArray objectAtIndex:i] objectForKey:@"image_url"] forKey:@"image_url"]; 
       [listViewArray addObject:tempDict]; 

      } 
      [tempDict release]; 
      tempDict = nil; 
     } 


     if (![appDelegate.managedObjectContext save:&error]) { 
      NSLog(@"Core Data Error - %@", [error localizedDescription]); 

     } 

     //  NSMutableArray *tempArray = [NSMutableArray arrayWithArray:listViewArray]; 
     //  [listViewArray removeAllObjects]; 
     //  [listViewArray addObjectsFromArray:[[tempArray reverseObjectEnumerator] allObjects]]; 
     //  tempArray = nil; 
    } 
    [tempListArray release]; 
} 
[mListsTableView reloadData]; 

這裏是崩潰的代碼,稱爲解析器:foundObject:循環代碼已被刪除,因爲這就是所謂的每一次下載新的Json對象。

[jsonStreamedData addObject:dict]; 
    if (!listViewArray) 
     listViewArray = [[NSMutableArray alloc] init]; 

    MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate]; 
    NSError *error = nil; 
    [appDelegate.managedObjectContext reset]; 

    NSManagedObjectContext *managedObjectContext = appDelegate.managedObjectContext; 

    NSFetchRequest *checkRequest = [[NSFetchRequest alloc] init]; 
    NSEntityDescription *lessonEntity = [NSEntityDescription entityForName:@"Lessons" inManagedObjectContext:managedObjectContext]; 
    [checkRequest setEntity:lessonEntity]; 
    NSPredicate *langPredicate = [NSPredicate predicateWithFormat:@"(language = %@)", appDelegate.currentLanguage]; 
    NSPredicate *userPredicate = [NSPredicate predicateWithFormat:@"(username = %@)", appDelegate.userName]; 
    NSPredicate *idPredicate = [NSPredicate predicateWithFormat:@"(content_Id = %@)", [dict valueForKey:@"id"]]; 
    [checkRequest setPredicate:[NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:langPredicate, userPredicate, idPredicate, nil]]]; 

    NSArray *checkResults = [managedObjectContext executeFetchRequest:checkRequest error:&error]; 
    [checkRequest release]; 

    NSMutableDictionary *tempDict = [[NSMutableDictionary alloc] init]; 

    if ([checkResults count]) { 
     Lessons *lessonObj = [checkResults objectAtIndex:0]; 
     lessonObj.cards_count = [dict valueForKey:@"cards_count"]; 
     lessonObj.mTitle = [dict valueForKey:@"title"]; 
     lessonObj.sound_Url = [dict valueForKey:@"audio_url"]; 
     lessonObj.mId = [NSNumber numberWithInt:jsonStreamedData.count - 1]; // This should be equivalent to i from the loop in the first code 
     [tempDict setValue:lessonObj forKey:@"lesson"]; 
     [tempDict setValue: [dict objectForKey:@"image_url"] forKey:@"image_url"]; 
     [listViewArray addObject:tempDict]; 

    } 
    else { 

     Lessons *newLesson = (Lessons *)[NSEntityDescription insertNewObjectForEntityForName:@"Lessons" inManagedObjectContext:appDelegate.managedObjectContext]; 
     newLesson.cards_count = [dict valueForKey:@"cards_count"]; 
     newLesson.mTitle = [dict valueForKey:@"title"]; 
     newLesson.sound_Url = [dict valueForKey:@"audio_url"]; 
     newLesson.content_Id = [dict valueForKey:@"id"]; 
     newLesson.username = appDelegate.userName; 
     newLesson.language = appDelegate.currentLanguage; 
     newLesson.mId = [NSNumber numberWithInt:jsonStreamedData.count - 1]; 
     [tempDict setValue:newLesson forKey:@"lesson"]; 
     [tempDict setValue: [dict objectForKey:@"image_url"] forKey:@"image_url"]; 
     [listViewArray addObject:tempDict]; 

    } 

    [tempDict release]; 
    tempDict = nil; 

    if (![appDelegate.managedObjectContext save:&error]) { 
     ALog(@"Core Data Error - %@", [error localizedDescription]); 
    } 

    // NSMutableArray *tempArray = [NSMutableArray arrayWithArray:listViewArray]; 
    // [listViewArray removeAllObjects]; 
    // [listViewArray addObjectsFromArray:[[tempArray reverseObjectEnumerator] allObjects]]; 
    // tempArray = nil; 
//[self getListsLocally]; 
[mListsTableView reloadData]; 

最後,這裏是使用第二上市的時候,在那的tableView崩潰的實際部分:的cellForRowAtIndexPath:順便說一下,它崩潰的時候排== 1,不排== 0,出於某種原因,排0沒問題......當然,它從來沒有機會加載其他行。

 titleLabel.text = [[[listViewArray objectAtIndex:indexPath.row] valueForKey:@"lesson"] valueForKey:@"mTitle"]; // CRASH! 
     labelCards.text = [NSString stringWithFormat:@"%@ Cards", [[[listViewArray objectAtIndex:indexPath.row] valueForKey:@"lesson"] valueForKey:@"cards_count"]]; 

     if([[listViewArray objectAtIndex:indexPath.row] objectForKey:@"userImageObj"] == nil){ 
      mImageView.backgroundColor = [UIColor grayColor]; 
      if ([[listViewArray objectAtIndex:indexPath.row] objectForKey:@"isThreadLaunched"] == nil) { 
       [NSThread detachNewThreadSelector:@selector(loadImagesInBackground:) toTarget:self withObject:[NSNumber numberWithInt:indexPath.row]]; 
       [[listViewArray objectAtIndex:indexPath.row] setObject:@"Yes" forKey:@"isThreadLaunched"]; 
      } 
     }else { 
      mImageView.image = [[listViewArray objectAtIndex:indexPath.row] objectForKey:@"userImageObj"]; 
     } 

回答

3

的當您在運行提取之前立即在managedObjectContext上調用reset時,最有可能發生對象失效。調用reset將使內存中的對象失效,但在保存之前不會刪除它們。如果失效的託管對象由另一個對象(如數組)保留,則它將持續保存在失效的表單中。當您運行提取時,提取會返回導致錯誤的無效對象,當您嘗試訪問它們的某個屬性時

reset與撤銷管理器一起使用時會被調用。這不是一個通用的,「擦除上下文」的電話。如果你想刪除現有的對象,你需要獲取它們並明確地刪除它們。

您的代碼還有其他一些問題。您在checkRequest陣列上撥打release即使您沒有創建它。這可能會導致陣列隨機消失。同樣,listViewArray似乎是一個類屬性,但你從不使用訪問器形式,例如self.listViewArray以確保適當的保留。

1

最有可能的原因是解析器沒有在主線程上運行,而您正在使用跨主線程和分析器相同ManagedObjectContext - 你不能做到這一點 - 保證引起各種奇怪的行爲。

您需要創建解析器新ManagedObjectContext和準與的persistentStore主線程的ManagedObjectContext

的CoreData框架是很清楚這個 - 你不能跨線程邊界共享ManagedObjectContext的