我已经编写了一个应用程序,允许其用户“最喜欢”#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];
}
答案 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