变异方法发送到不可变对象?

时间:2015-06-15 23:53:53

标签: ios objective-c uitableview

我已经编写了一个应用程序,允许其用户“最喜欢”#34; tableview中的项目。例如。点击按钮可将选定单元格中的项目添加到收藏夹表格视图中。以下代码完美运行,直到我更新了我的Xcode版本。现在,当点击星形按钮时,我收到以下错误:

  

由于未捕获的异常而终止应用   ' NSInternalInconsistencyException',原因:' - [__ NSCFDictionary   setObject:forKey:]:发送到不可变对象的变异方法'

有谁知道为什么会这样?我所有必要的属性都是可变的。

感谢。

AppDelegate.h

@interface AppDelegate : UIResponder <UIApplicationDelegate>

@property (strong,nonatomic) NSMutableData *data;
@property (strong,nonatomic) NSMutableArray *Strains;
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) NSMutableArray *strainsfinal;
-(void)updateStrains;
@end

AppDelegate.m

#import "AppDelegate.h"
#import <GoogleMaps/GoogleMaps.h>
#import <QuartzCore/QuartzCore.h>

@implementation AppDelegate

@synthesize window = _window;
@synthesize data;
@synthesize Strains;
@synthesize strainsfinal;
-(void)applicationWillResignActive:(UIApplication *)application{
    [self updateStrains];
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Add in your API key here:
    [GMSServices provideAPIKey:@"KEY HERE"];


    strainsfinal = [[NSMutableArray alloc] init];
    NSObject *testObject = [[NSUserDefaults standardUserDefaults] objectForKey:@"strains"];
    if (testObject != nil) {
        [strainsfinal addObjectsFromArray:[[[NSUserDefaults standardUserDefaults] arrayForKey:@"strains"] mutableCopy]];

                return YES;
    }

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
    data = [[NSMutableData alloc] init];

}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData
{
    [data appendData:theData];

}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
   [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    NSMutableArray *oldStrains = [[NSMutableArray alloc] initWithArray:strainsfinal];

    NSLog(@"count is %i", oldStrains.count);
    NSMutableArray *blankArray = [[NSMutableArray alloc] init];

      Strains = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error:nil];


    if (oldStrains.count < Strains.count) {
        for (int x = oldStrains.count; x < Strains.count; x++) {
            [oldStrains addObject:[Strains objectAtIndex:x]];
        }


    }
    if (oldStrains.count > 0) {
        NSLog(@"count is %i",oldStrains.count);

        blankArray = [oldStrains copy];

        NSSortDescriptor *sortDescriptor = nil;

        sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"Title"
                                                     ascending:YES];
        NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
        Strains = [blankArray  sortedArrayUsingDescriptors:sortDescriptors];

        int count = 0;
        for (NSDictionary *item in Strains) {
            [item setValue:[NSNumber numberWithInt:count] forKey:@"position"];
            if ([item valueForKey:@"checked"] ==  nil) {
                bool checked = NO;
                [item setValue:[NSNumber numberWithBool:checked]  forKey:@"checked"];
            }
            count++;
        }

        [strainsfinal removeAllObjects];
        [strainsfinal addObjectsFromArray:[Strains mutableCopy]];
    }

}

- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
    UIAlertView *errorView = [[UIAlertView alloc] initWithTitle:@"Error" message:@"The download could not complete - please make sure that you're connected to 3G or Wi-Fi." delegate:nil
                                              cancelButtonTitle:@"Dismiss" otherButtonTitles:nil];
    [errorView show];
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;    
}

-(void)updateStrains {

    [[NSUserDefaults standardUserDefaults] setObject:strainsfinal forKey:@"strains"];
    NSLog(@"updated strains %@",strainsfinal);
    [[NSUserDefaults standardUserDefaults] synchronize]; // this will save you UserDefaults

}

ViewController.h

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

@interface ViewController : UIViewController <UITableViewDelegate,UITableViewDataSource,UISearchBarDelegate>
{
    AppDelegate *appdelegate;
    NSIndexPath *currentDetailPath;
    UINavigationController *navController;
    bool loaded;
    NSArray *searchResults;
    NSMutableData *data;
    NSMutableArray *dataArray;

}

@property (nonatomic, strong) NSMutableArray *Strains;
@property (nonatomic, strong) NSMutableSet *favoritesArray;
@property (nonatomic, strong) NSMutableSet *strainsFiltered;
@property (nonatomic, retain) NSArray *searchResults;
@property (strong, nonatomic) IBOutlet UITableView *StrainTableView;

@end

ViewController.m

    @implementation ViewController
    @synthesize searchResults;
    @synthesize StrainTableView;
    @synthesize favoritesArray;
    @synthesize Strains;

    - (void)viewDidLoad
    {
        [super viewDidLoad];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(arrayUpdated)
                                                     name:@"arrayupdated"
                                                   object:nil];
    }

    -(void)arrayUpdated
    {
       Strains = appdelegate.strainsfinal;
        [StrainTableView reloadData];

    }

    - (int)numberOfSectionsInTableView: (UITableView *)tableview
    {
        return 1;
    }

    - (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
        if (tableView == self.searchDisplayController.searchResultsTableView) {
            return [searchResults count];
        } else {
            return [Strains count];
        }
    }

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {

        static NSString *customerTableIdentifier = @"StrainTableCell";

        StrainTableCell *cell = (StrainTableCell *)[StrainTableView dequeueReusableCellWithIdentifier:customerTableIdentifier];
        if (cell == nil)

            cell = [[StrainTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:customerTableIdentifier];

        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
        tableView.separatorStyle = UITableViewCellSeparatorStyleNone;


        NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"StrainTableCell" owner:self options:nil];
        cell = [nib objectAtIndex:0];

        if (tableView == self.searchDisplayController.searchResultsTableView) {
            NSLog(@"Using the search results");

            cell.titleLabel.text = [[searchResults objectAtIndex:indexPath.row] objectForKey:@"Title"];

            cell.descriptionLabel.text = [[searchResults objectAtIndex:indexPath.row] objectForKey:@"Description"];

            cell.ratingLabel.text = [[searchResults objectAtIndex:indexPath.row] objectForKey:@"Rating"];


        } else {

            cell.titleLabel.text = [[Strains objectAtIndex:indexPath.row] objectForKey:@"Title"];

            cell.descriptionLabel.text = [[Strains objectAtIndex:indexPath.row] objectForKey:@"Description"];

         //   cell.ailmentLabel.text = [[Strains objectAtIndex:indexPath.row] objectForKey:@"customeremail"];

            cell.ratingLabel.text = [[Strains objectAtIndex:indexPath.row] objectForKey:@"Rating"];

        }

        NSMutableDictionary *item = [Strains objectAtIndex:indexPath.row];
        cell.textLabel.text = [item objectForKey:@"text"];

        //  [item setObject:cell forKey:@"CustomerCell"];
        BOOL checked = [[item objectForKey:@"checked"] boolValue];


        UIImage *image = (checked) ? [UIImage   imageNamed:@"checked.png"] : [UIImage imageNamed:@"unchecked.png"];
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        CGRect frame = CGRectMake(0.0, 0.0, image.size.width, image.size.height);
        button.frame = frame;
        [button setBackgroundImage:image forState:UIControlStateNormal];
        [button addTarget:self action:@selector(checkButtonTapped:event:)  forControlEvents:UIControlEventTouchUpInside];
        button.backgroundColor = [UIColor clearColor];
        cell.accessoryView = button;

        return cell;

    }

    -(void)notificationCheckButtonTapped{

        NSIndexPath *indexPath = currentDetailPath;

        if (indexPath != Nil)
        {

            NSMutableDictionary *item = [Strains objectAtIndex:indexPath.row];
            BOOL isItChecked =  [[item objectForKey:@"checked"] boolValue];
     NSMutableArray *quickArray = [[NSMutableArray alloc] initWithArray:Strains];
            [quickArray replaceObjectAtIndex:indexPath.row withObject:item];

            [item setObject:[NSNumber numberWithBool:!isItChecked] forKey:@"checked"];
       Strains = [quickArray copy];

      [StrainTableView reloadData];

        }

    }

    - (void)checkButtonTapped:(id)sender event:(id)event
    {
        NSLog(@"made it here and event is %@",event);

        NSSet *touches = [event allTouches];
        UITouch *touch = [touches anyObject];
        CGPoint currentTouchPosition = [touch locationInView:self.StrainTableView];
        NSIndexPath *  indexPath ;
        indexPath =  [self.StrainTableView indexPathForRowAtPoint: currentTouchPosition];
        NSLog(@"indexpath is below");
        NSLog(@"%@",indexPath);
        if (indexPath != Nil)
        {

            NSMutableDictionary *item = [Strains objectAtIndex:indexPath.row];
            BOOL isItChecked =  [[item objectForKey:@"checked"] boolValue];
      NSMutableArray *quickArray = [[NSMutableArray alloc] initWithArray:Strains];
            [quickArray replaceObjectAtIndex:indexPath.row withObject:item];


            [item setObject:[NSNumber numberWithBool:!isItChecked] forKey:@"checked"];
            Strains = [quickArray copy];

            [StrainTableView reloadData];

        }

    }
    - (void)viewWillAppear:(BOOL)animated
    {
        [super viewWillAppear:animated];
        // if (loaded == NO) {

        appdelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
        Strains = [appdelegate.strainsfinal copy];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(notificationCheckButtonTapped)
                                                     name:@"detailChecked"
                                                   object:nil];

        if (Strains == Nil) {
            Strains = [[NSMutableSet alloc] initWithArray:appdelegate.strainsfinal];
        }

        if ([[NSUserDefaults standardUserDefaults] objectForKey:@"favorites"] != Nil) {

            NSData *dataSave = [[NSUserDefaults standardUserDefaults] objectForKey:@"favorites"];
            favoritesArray =  [NSKeyedUnarchiver unarchiveObjectWithData:dataSave];
        }
  }
        loaded = YES;
        [StrainTableView reloadData];

    }

3 个答案:

答案 0 :(得分:3)

这些行或类似行是问题的根源:

for (NSDictionary *item in Strains) {
    [item setValue:[NSNumber numberWithInt:count] forKey:@"position"];
    // ...
}

假设Strains由NSDictionary对象组成,你是对的,你不能对NSDictionary说setValue:forKey:。这是不可改变的。您需要从NSDictionary派生NSMutableDictionary并将setValue:forKey:(或更好,setObject:forKey:)说成。

答案 1 :(得分:2)

声明

blankArray = [oldStrains copy];  

blankArray的内容变得不可变。

然后在代码中:

for (NSDictionary *item in Strains) {
    [item setValue:[NSNumber numberWithInt:count] forKey:@"position"];

item将是一个不可变的NSDictionary

尝试:     blankArray = [oldStrains mutableCopy];

checkButtonTapped:中的部分可能简化为:

NSMutableDictionary *item = [Strains objectAtIndex:indexPath.row];
BOOL isItChecked = [[item objectForKey:@"checked"] boolValue];
[item setObject:[NSNumber numberWithBool:!isItChecked] forKey:@"checked"];
[Strains replaceObjectAtIndex:indexPath.row withObject:item];

答案 2 :(得分:0)

在某个地方,你宣称它是NSDictionary,而你打算将它作为NSMutableDictionary