iOS NSMutableArray 值在离开 AlertView 后变为 nil
iOS NSMutableArray value turns to nil after leaving AlertView
我正在为 iOS 9.0 编写一个应用程序,但我 运行 遇到了一个错误,我无法解决以挽救我的生命!简而言之,当我在 AlertView 中将项目添加到 NSMutableArray wishlist 时,wishlist 包含其所有元素,包括新添加的元素。但是,当我调用 [self.tableView reloadData] 时,有时新值的所有成员数据都设置为 nil。需要明确的是,wishlist in ...reloadData... 确实有 n+1 个元素,但最后一个有时为 nil。
例如,添加带有 food_item 参数 "test" 或 "test 1" 的对象就可以了。但是输入 "test 2 2 a" 会导致数据在重新加载时被设置为 nil。
我试图通过在我的 .h 文件中将 wishlist 设为强的非原子 属性 来解决这个问题,但这没有用。我可以确认数据库方面工作得很好,因为如果我在重新加载 tableview 数据之前重置 wishlist 中的所有值,我的所有元素都在那里(但这会导致一些令人讨厌的滞后).
有什么想法吗?
#import "WishlistViewController.h"
#import "usersDAO.h"
#import "wishlistitemDAO.h"
#import "wishlistDAO.h"
#import "TabBarController.h"
#import "MGSwipeButton.h"
#import "MGSwipeTableCell.h"
@interface WishlistViewController ()
@end
@implementation WishlistViewController{
NSMutableArray *wishlist;
}
@synthesize username, house_name, house_id, navBar, admin;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
TabBarController *tabBar = (TabBarController *)self.tabBarController;
self.username = tabBar.username;
self.house_id = tabBar.house_id;
self.house_name = tabBar.house_name;
self.admin = tabBar.admin;
wishlistitemDAO *wliDAO = [wishlistitemDAO new];
wishlistDAO *wlDAO = [wishlistDAO new];
self->wishlist = [[NSMutableArray alloc] init];
wlDAO.id = [wlDAO getCurrentWishlistFromHouseId:house_id];
self->wishlist = [wliDAO getWishlistItemsWithWishlistId:wlDAO.id];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [wishlist count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
wishlistitemDAO *wliDAO = [wishlistitemDAO new];
static NSString * reuseIdentifier = @"programmaticCell";
MGSwipeTableCell *cell = [self.tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (!cell) {
cell = [[MGSwipeTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
}
cell.textLabel.text = [wliDAO getFoodItem:[wishlist objectAtIndex:indexPath.row]];
//configure left buttons
cell.leftButtons = @[[MGSwipeButton
buttonWithTitle:[NSString stringWithFormat:@"%@",[wliDAO getQuantity:[wishlist objectAtIndex:indexPath.row]]]
backgroundColor:[UIColor greenColor] setClickable:false],
[MGSwipeButton
buttonWithTitle:@"" icon:[UIImage imageNamed:@"thumbs-up.png"] backgroundColor:[UIColor cyanColor]],
[MGSwipeButton
buttonWithTitle:@"" icon:[UIImage imageNamed:@"thumbs-down.png"] backgroundColor:[UIColor redColor]]
];
cell.leftSwipeSettings.transition = MGSwipeTransition3D;
if([admin isEqualToNumber:[NSNumber numberWithInt:1]]){
//configure right buttons
cell.rightButtons = @[[MGSwipeButton buttonWithTitle:@""
icon:[UIImage imageNamed:@"trash-can.png"] backgroundColor:[UIColor redColor]
callback:^BOOL(MGSwipeTableCell *sender) {
wishlistitemDAO *wliDAO = [wishlistitemDAO new];
[wliDAO removeItemFromWishlist:[wliDAO getId:[wishlist objectAtIndex:indexPath.row]]];
[self->wishlist removeObjectAtIndex:indexPath.row];
//[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];
return 1;
}],
[MGSwipeButton buttonWithTitle:@""
icon:[UIImage imageNamed:@"shopping-cart-add.png"] backgroundColor:[UIColor cyanColor]
]
];
cell.rightSwipeSettings.transition = MGSwipeTransition3D;
}
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"wishlistLogoutSegue"]){
}
}
- (IBAction)addItem:(id)sender {
//alert view
UIAlertView *message = [[UIAlertView alloc]
initWithTitle: @"New Item"
message: @""
delegate: self
cancelButtonTitle: @"Cancel"
otherButtonTitles: @"Add",nil];
message.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
[[message textFieldAtIndex:1] setSecureTextEntry:false];
[[message textFieldAtIndex:0] setPlaceholder:@"Item Name"];
[[message textFieldAtIndex:1] setPlaceholder:@"Quantity"];
[message show];
}
//which button was clicked by the user
-(void)alertView: (UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
switch(buttonIndex) {
case 0:{
break;
}
case 1:{
UITextField *food_item = [alertView textFieldAtIndex: 0];
UITextField *quantity = [alertView textFieldAtIndex:1];
wishlistitemDAO *wliDAO = [wishlistitemDAO new];
wishlistDAO *wlDAO = [wishlistDAO new];
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
f.numberStyle = NSNumberFormatterDecimalStyle;
NSNumber *n = [f numberFromString:quantity.text];
[wliDAO addWishlistItem:food_item.text withQuantity:n toWishlistId:[wlDAO getCurrentWishlistFromHouseId:house_id]];
wlDAO.id = [wlDAO getCurrentWishlistFromHouseId:house_id];
[wishlist addObject:[[wliDAO getWishlistItemsWithWishlistId:wlDAO.id] lastObject]];
[self.tableView reloadData];
break;
}
default:
break;
}
}
@end
首先,使用 self->wishlist
访问像 wishlist
这样的成员很奇怪,移除 self->
因为 wishlist
单独 在 class 的方法中引用它。它的作用就像一个强大的属性。但是由于您已经在使用 username
等属性,因此最好将 wishlist
改为 属性 并保持一致:
@interface WishlistViewController ()
@property (nonatomic, strong) NSMutableArray *wishlist
@end
@implementation WishlistViewController
// @synthesize line is redundant since 2012
- (void)viewDidLoad {
...
(在我下面引用的你的代码中,我假设你已经这样做了,我将 self->
替换为 self.
因为前者会伤害我的大脑来打字或偶看看。)
现在的答案是,Carl 提到分配 wishlist
的 viewDidLoad
行时确实是正确的。很明显你可能有一些误解,因为你正在创建一个数组,然后在两行之后替换它:
self.wishlist = [[NSMutableArray alloc] init]; // CREATE
wlDAO.id = [wlDAO getCurrentWishlistFromHouseId:house_id];
self.wishlist = [wliDAO getWishlistItemsWithWishlistId:wlDAO.id]; // STOMP
您的意思可能是用 wishlistDAO
的结果填充数组,例如:
self.wishlist = [[NSMutableArray alloc] init];
wlDAO.id = [wlDAO getCurrentWishlistFromHouseId:house_id];
[self.wishlist addObjectsFromArray:[wliDAO getWishlistItemsWithWishlistId:wlDAO.id]];
但是您也可以像 Carl 建议的那样删除第一个 wishlist =
行并制作方法结果的可变副本,但是像 self.wishlist = [wliDAO getWishlistItemsWithWishlistId:wlDAO.id].mutableCopy;
可能发生的事情是 wishlistDAO
正在 returning 它的内部可变数组,它可能会在以后使用和更改,这就是 wishlist
设置的内容,即.设置为参考,即。 wishlistDAO
自己的 NSMutableArray
对象的相同指针值。这很糟糕,这是上面建议的更改所解决的问题。然后在您稍后再次调用相同的方法时:
[wishlist addObject:[[wliDAO getWishlistItemsWithWishlistId:wlDAO.id] lastObject]];
我猜它是从擦除它的数组开始的,这会擦除你的数组,因为它是同一个对象。也许它也会释放它并在此过程中创建一个新的,留下 wishlist
作为现在空数组的唯一保留。
为了安全并遵守最佳实践:
getWishlistItems
应该 return 数组的新副本,而不是对某些内部 属性 的引用,或者以任何其他方式可能会在 [=65] 之后更改=]编辑。该方法的结果应声明为不可变 NSArray
.
- 如上建议,您的视图控制器应该对该方法的结果使用
addObjectsFromArray
,或者将 wishlist
分配给它的 mutableCopy
。
单独执行其中一项应该可以解决您的问题。这肯定是造成这 2 个错误的组合。
我正在为 iOS 9.0 编写一个应用程序,但我 运行 遇到了一个错误,我无法解决以挽救我的生命!简而言之,当我在 AlertView 中将项目添加到 NSMutableArray wishlist 时,wishlist 包含其所有元素,包括新添加的元素。但是,当我调用 [self.tableView reloadData] 时,有时新值的所有成员数据都设置为 nil。需要明确的是,wishlist in ...reloadData... 确实有 n+1 个元素,但最后一个有时为 nil。
例如,添加带有 food_item 参数 "test" 或 "test 1" 的对象就可以了。但是输入 "test 2 2 a" 会导致数据在重新加载时被设置为 nil。
我试图通过在我的 .h 文件中将 wishlist 设为强的非原子 属性 来解决这个问题,但这没有用。我可以确认数据库方面工作得很好,因为如果我在重新加载 tableview 数据之前重置 wishlist 中的所有值,我的所有元素都在那里(但这会导致一些令人讨厌的滞后).
有什么想法吗?
#import "WishlistViewController.h"
#import "usersDAO.h"
#import "wishlistitemDAO.h"
#import "wishlistDAO.h"
#import "TabBarController.h"
#import "MGSwipeButton.h"
#import "MGSwipeTableCell.h"
@interface WishlistViewController ()
@end
@implementation WishlistViewController{
NSMutableArray *wishlist;
}
@synthesize username, house_name, house_id, navBar, admin;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
TabBarController *tabBar = (TabBarController *)self.tabBarController;
self.username = tabBar.username;
self.house_id = tabBar.house_id;
self.house_name = tabBar.house_name;
self.admin = tabBar.admin;
wishlistitemDAO *wliDAO = [wishlistitemDAO new];
wishlistDAO *wlDAO = [wishlistDAO new];
self->wishlist = [[NSMutableArray alloc] init];
wlDAO.id = [wlDAO getCurrentWishlistFromHouseId:house_id];
self->wishlist = [wliDAO getWishlistItemsWithWishlistId:wlDAO.id];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [wishlist count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
wishlistitemDAO *wliDAO = [wishlistitemDAO new];
static NSString * reuseIdentifier = @"programmaticCell";
MGSwipeTableCell *cell = [self.tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
if (!cell) {
cell = [[MGSwipeTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:reuseIdentifier];
}
cell.textLabel.text = [wliDAO getFoodItem:[wishlist objectAtIndex:indexPath.row]];
//configure left buttons
cell.leftButtons = @[[MGSwipeButton
buttonWithTitle:[NSString stringWithFormat:@"%@",[wliDAO getQuantity:[wishlist objectAtIndex:indexPath.row]]]
backgroundColor:[UIColor greenColor] setClickable:false],
[MGSwipeButton
buttonWithTitle:@"" icon:[UIImage imageNamed:@"thumbs-up.png"] backgroundColor:[UIColor cyanColor]],
[MGSwipeButton
buttonWithTitle:@"" icon:[UIImage imageNamed:@"thumbs-down.png"] backgroundColor:[UIColor redColor]]
];
cell.leftSwipeSettings.transition = MGSwipeTransition3D;
if([admin isEqualToNumber:[NSNumber numberWithInt:1]]){
//configure right buttons
cell.rightButtons = @[[MGSwipeButton buttonWithTitle:@""
icon:[UIImage imageNamed:@"trash-can.png"] backgroundColor:[UIColor redColor]
callback:^BOOL(MGSwipeTableCell *sender) {
wishlistitemDAO *wliDAO = [wishlistitemDAO new];
[wliDAO removeItemFromWishlist:[wliDAO getId:[wishlist objectAtIndex:indexPath.row]]];
[self->wishlist removeObjectAtIndex:indexPath.row];
//[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[self.tableView reloadData];
return 1;
}],
[MGSwipeButton buttonWithTitle:@""
icon:[UIImage imageNamed:@"shopping-cart-add.png"] backgroundColor:[UIColor cyanColor]
]
];
cell.rightSwipeSettings.transition = MGSwipeTransition3D;
}
return cell;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if([segue.identifier isEqualToString:@"wishlistLogoutSegue"]){
}
}
- (IBAction)addItem:(id)sender {
//alert view
UIAlertView *message = [[UIAlertView alloc]
initWithTitle: @"New Item"
message: @""
delegate: self
cancelButtonTitle: @"Cancel"
otherButtonTitles: @"Add",nil];
message.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput;
[[message textFieldAtIndex:1] setSecureTextEntry:false];
[[message textFieldAtIndex:0] setPlaceholder:@"Item Name"];
[[message textFieldAtIndex:1] setPlaceholder:@"Quantity"];
[message show];
}
//which button was clicked by the user
-(void)alertView: (UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
switch(buttonIndex) {
case 0:{
break;
}
case 1:{
UITextField *food_item = [alertView textFieldAtIndex: 0];
UITextField *quantity = [alertView textFieldAtIndex:1];
wishlistitemDAO *wliDAO = [wishlistitemDAO new];
wishlistDAO *wlDAO = [wishlistDAO new];
NSNumberFormatter *f = [[NSNumberFormatter alloc] init];
f.numberStyle = NSNumberFormatterDecimalStyle;
NSNumber *n = [f numberFromString:quantity.text];
[wliDAO addWishlistItem:food_item.text withQuantity:n toWishlistId:[wlDAO getCurrentWishlistFromHouseId:house_id]];
wlDAO.id = [wlDAO getCurrentWishlistFromHouseId:house_id];
[wishlist addObject:[[wliDAO getWishlistItemsWithWishlistId:wlDAO.id] lastObject]];
[self.tableView reloadData];
break;
}
default:
break;
}
}
@end
首先,使用 self->wishlist
访问像 wishlist
这样的成员很奇怪,移除 self->
因为 wishlist
单独 在 class 的方法中引用它。它的作用就像一个强大的属性。但是由于您已经在使用 username
等属性,因此最好将 wishlist
改为 属性 并保持一致:
@interface WishlistViewController ()
@property (nonatomic, strong) NSMutableArray *wishlist
@end
@implementation WishlistViewController
// @synthesize line is redundant since 2012
- (void)viewDidLoad {
...
(在我下面引用的你的代码中,我假设你已经这样做了,我将 self->
替换为 self.
因为前者会伤害我的大脑来打字或偶看看。)
现在的答案是,Carl 提到分配 wishlist
的 viewDidLoad
行时确实是正确的。很明显你可能有一些误解,因为你正在创建一个数组,然后在两行之后替换它:
self.wishlist = [[NSMutableArray alloc] init]; // CREATE
wlDAO.id = [wlDAO getCurrentWishlistFromHouseId:house_id];
self.wishlist = [wliDAO getWishlistItemsWithWishlistId:wlDAO.id]; // STOMP
您的意思可能是用 wishlistDAO
的结果填充数组,例如:
self.wishlist = [[NSMutableArray alloc] init];
wlDAO.id = [wlDAO getCurrentWishlistFromHouseId:house_id];
[self.wishlist addObjectsFromArray:[wliDAO getWishlistItemsWithWishlistId:wlDAO.id]];
但是您也可以像 Carl 建议的那样删除第一个 wishlist =
行并制作方法结果的可变副本,但是像 self.wishlist = [wliDAO getWishlistItemsWithWishlistId:wlDAO.id].mutableCopy;
可能发生的事情是 wishlistDAO
正在 returning 它的内部可变数组,它可能会在以后使用和更改,这就是 wishlist
设置的内容,即.设置为参考,即。 wishlistDAO
自己的 NSMutableArray
对象的相同指针值。这很糟糕,这是上面建议的更改所解决的问题。然后在您稍后再次调用相同的方法时:
[wishlist addObject:[[wliDAO getWishlistItemsWithWishlistId:wlDAO.id] lastObject]];
我猜它是从擦除它的数组开始的,这会擦除你的数组,因为它是同一个对象。也许它也会释放它并在此过程中创建一个新的,留下 wishlist
作为现在空数组的唯一保留。
为了安全并遵守最佳实践:
getWishlistItems
应该 return 数组的新副本,而不是对某些内部 属性 的引用,或者以任何其他方式可能会在 [=65] 之后更改=]编辑。该方法的结果应声明为不可变NSArray
.- 如上建议,您的视图控制器应该对该方法的结果使用
addObjectsFromArray
,或者将wishlist
分配给它的mutableCopy
。
单独执行其中一项应该可以解决您的问题。这肯定是造成这 2 个错误的组合。