简体   繁体   中英

NSMutableArray adding/removing objects + NSUserDefaults

I want to make a "Favorites" in my RSS reader. My RSS parser is parsing a RSS feed to NSMutableArray, and then object "item" is created from part of my rss (selected post). My code:

//Creating mutable array and adding items:

    - (void)viewDidLoad {
        if (favoritedAlready == nil) {
            favoritedAlready = [[NSMutableArray alloc] init];
            [[NSUserDefaults standardUserDefaults] setObject:favoritedAlready forKey:@"favorites"];
            [[NSUserDefaults standardUserDefaults] synchronize];
            NSLog(@"избранное с нуля");
        }
    }
    - (void) addToFavorites {
        NSMutableArray* favoritedAlready = [[NSUserDefaults standardUserDefaults] objectForKey:@"favorites"];
        [favoritedAlready addObject: item];
        [[NSUserDefaults standardUserDefaults] setObject:favoritedAlready forKey:@"favorites"];
        [[NSUserDefaults standardUserDefaults] synchronize];

        NSLog(@"Добавлено в избранное. В избранном %i статей", [favoritedAlready count]);
    }

    //Removing items (another View)
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        rssItems = [[NSUserDefaults standardUserDefaults] objectForKey:@"favorites"];
        [self.tableView reloadData];
        NSLog(@"Загрузилось избранное, %i избранных статей", [rssItems count]);
    }
    - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
        NSLog(@"Номерок строчки в которой удаляемый объект %i", indexPath.row+1);
        [rssItems removeObjectAtIndex:indexPath.row];
        [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];

        [[NSUserDefaults standardUserDefaults] setObject:rssItems forKey:@"favorites"];
        [[NSUserDefaults standardUserDefaults] synchronize];
    }

It worked perfectly initially, but when I added and removed items, it started crashing. Crash logs: I added object to Favorites and removed it:

2011-09-25 20:14:44.534 ARSSReader[36211:11303] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray removeObjectAtIndex:]: mutating method sent to immutable object'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x015505a9 __exceptionPreprocess + 185
    1   libobjc.A.dylib                     0x016a4313 objc_exception_throw + 44
    2   CoreFoundation                      0x01508ef8 +[NSException raise:format:arguments:] + 136
    3   CoreFoundation                      0x01508e6a +[NSException raise:format:] + 58
    4   CoreFoundation                      0x01547dd1 -[__NSCFArray removeObjectAtIndex:] + 193
    5   ARSSReader                          0x000a0ced -[FavoritesView tableView:commitEditingStyle:forRowAtIndexPath:] + 173
    6   UIKit                               0x00876037 -[UITableView(UITableViewInternal) animateDeletionOfRowWithCell:] + 101
    7   UIKit                               0x0080b4fd -[UIApplication sendAction:to:from:forEvent:] + 119
    8   UIKit                               0x0089b799 -[UIControl sendAction:to:forEvent:] + 67
    9   UIKit                               0x0089dc2b -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 527
    10  UIKit                               0x0089c7d8 -[UIControl touchesEnded:withEvent:] + 458
    11  UIKit                               0x0082fded -[UIWindow _sendTouchesForEvent:] + 567
    12  UIKit                               0x00810c37 -[UIApplication sendEvent:] + 447
    13  UIKit                               0x00815f2e _UIApplicationHandleEvent + 7576
    14  GraphicsServices                    0x01c91992 PurpleEventCallback + 1550
    15  CoreFoundation                      0x01531944 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 52
    16  CoreFoundation                      0x01491cf7 __CFRunLoopDoSource1 + 215
    17  CoreFoundation                      0x0148ef83 __CFRunLoopRun + 979
    18  CoreFoundation                      0x0148e840 CFRunLoopRunSpecific + 208
    19  CoreFoundation                      0x0148e761 CFRunLoopRunInMode + 97
    20  GraphicsServices                    0x01c901c4 GSEventRunModal + 217
    21  GraphicsServices                    0x01c90289 GSEventRun + 115
    22  UIKit                               0x00819c93 UIApplicationMain + 1160
    23  ARSSReader                          0x00001e79 main + 121
    24  ARSSReader                          0x00001df5 start + 53
)
terminate called throwing an exception(gdb) 

But, if I added item, restarted app and then removed it, it worked perfectly.

I added item, restarted app, removed item, and tried to add new item:

2011-09-25 20:19:19.212 ARSSReader[36461:11303] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
*** Call stack at first throw:
(
    0   CoreFoundation                      0x015505a9 __exceptionPreprocess + 185
    1   libobjc.A.dylib                     0x016a4313 objc_exception_throw + 44
    2   CoreFoundation                      0x01508ef8 +[NSException raise:format:arguments:] + 136
    3   CoreFoundation                      0x01508e6a +[NSException raise:format:] + 58
    4   CoreFoundation                      0x01547cf1 -[__NSCFArray insertObject:atIndex:] + 209
    5   CoreFoundation                      0x01544c14 -[__NSCFArray addObject:] + 68
    6   ARSSReader                          0x00004b35 -[DetailsViewController addToFavorites] + 149
    7   UIKit                               0x0080b4fd -[UIApplication sendAction:to:from:forEvent:] + 119
    8   UIKit                               0x00a1dcc3 -[UIBarButtonItem(UIInternal) _sendAction:withEvent:] + 156
    9   UIKit                               0x0080b4fd -[UIApplication sendAction:to:from:forEvent:] + 119
    10  UIKit                               0x0089b799 -[UIControl sendAction:to:forEvent:] + 67
    11  UIKit                               0x0089dc2b -[UIControl(Internal) _sendActionsForEvents:withEvent:] + 527
    12  UIKit                               0x0089c7d8 -[UIControl touchesEnded:withEvent:] + 458
    13  UIKit                               0x0082fded -[UIWindow _sendTouchesForEvent:] + 567
    14  UIKit                               0x00810c37 -[UIApplication sendEvent:] + 447
    15  UIKit                               0x00815f2e _UIApplicationHandleEvent + 7576
    16  GraphicsServices                    0x01c91992 PurpleEventCallback + 1550
    17  CoreFoundation                      0x01531944 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 52
    18  CoreFoundation                      0x01491cf7 __CFRunLoopDoSource1 + 215
    19  CoreFoundation                      0x0148ef83 __CFRunLoopRun + 979
    20  CoreFoundation                      0x0148e840 CFRunLoopRunSpecific + 208
    21  CoreFoundation                      0x0148e761 CFRunLoopRunInMode + 97
    22  GraphicsServices                    0x01c901c4 GSEventRunModal + 217
    23  GraphicsServices                    0x01c90289 GSEventRun + 115
    24  UIKit                               0x00819c93 UIApplicationMain + 1160
    25  ARSSReader                          0x00001e79 main + 121
    26  ARSSReader                          0x00001df5 start + 53
)
terminate called throwing an exception(gdb) 

In addToFavorites

NSMutableArray* favoritedAlready = [[NSUserDefaults standardUserDefaults] objectForKey:@"favorites"];

will return an NSArray (it makes no difference it you save a mutable version), not an NSMutableArray

You need to create a mutable version:

NSMutableArray* favoritedAlready = [[[NSUserDefaults standardUserDefaults] objectForKey:@"favorites"] mutableCopy];

You can obviously not add items to a non-mutable array. Also just casting the return value to a NSMutableArray is wrong, it may work or it may not but that is irrelevant.

The issue is that NSUserDefaults only returns immutable arrays, even if you put in a mutable array:

Special Considerations : The returned array and its contents are immutable, even if the values you originally set were mutable.

Therefore, when you fetch your array from NSUserDefaults, you'll have to convert it by using the mutable copy method, or by creating a new NSMutableArray using the returned NSArray.


You may want to consolidate changes to fewer method calls to NSUserDefaults—perhaps in batches, perhaps at certain time intervals. Have a local array keep track of changes, then write the changes out, rather than grabbing the array from NSUserDefaults first. This will save one more trip to disk and will make your code more efficient as well.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM