【问题标题】:iOS NSMutableArray value turns to nil after leaving AlertViewiOS NSMutableArray 值在离开 AlertView 后变为 nil
【发布时间】:2015-11-12 22:11:37
【问题描述】:

我正在为 iOS 9.0 编写一个应用程序,但我遇到了一个我无法解决以挽救生命的错误!简而言之,当我在 AlertView 中向 NSMutableArray wishlist 添加一个项目时,wishlist 包含其所有元素,包括新添加的元素。但是,当我调用 [self.tableView reloadData] 时,有时新值会将其所有成员数据设置为 nil。需要明确的是,...reloadData... 中的 wishlist 确实有 n+1 个元素,但最后一个元素有时是 nil。

例如,添加带有 food_item 参数“test”或“test 1”的对象就可以了。但是输入“test 2 2 a”会导致数据在重新加载时设置为零。

我试图通过在我的 .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

【问题讨论】:

  • 您将 ivar 分配给 [wliDAO getWishlistItemsWithWishlistId:wlDAO.id] 的结果,它可能为 nil 或不可变。最初的 ivar 分配被吹走了。如果这确实返回了对 NSMutableArray 的引用,并且源在外部对其进行了修改,那么由于您只是对它的引用,它也会更改您的数据。您可能需要self->wishlist = [NSMutableArray arrayWithArray:[wliDAO getWishlistItemsWithWishlistId:wlDAO.id]];,它使用当时的数据创建数组的副本,并处理 nil。
  • 感谢您的回复!但是该函数处理空结果,所以如果有的话,它将返回一个空的 NSMutableArray。另外,我可以确认它确实返回了一个值,但是在 AlertView 中设置它和在 cellForRowAtIndexPath 中显示它之间的某个地方,它正在丢失。
  • 对,如果其他代码引用了同一个 NSMutableArray 实例,然后其他代码清空或修改了同一个实例,那么您的引用也将被清空。如果您关心数组的内容与特定的 NSMutableArray 实例,那么在它进入时制作一个副本。之后,唯一可以修改您的 ivar 内容的代码是您自己的类的代码。现在从理论上讲,其他代码可以修改您的 ivar 的 NSMutableArray 实例。如果上面的代码都不能做到这一点,那么其他代码可能是罪魁祸首。

标签: ios objective-c uitableview nsmutablearray


【解决方案1】:

首先,使用self->wishlist 访问像wishlist 这样的成员是很奇怪的,删除self-> 因为wishlist 它自己 在该类的方法中引用它。它就像一个强大的财产。但是由于您已经在使用 username 等的属性,因此最好将 wishlist 设为属性并保持一致:

@interface WishlistViewController ()
@property (nonatomic, strong) NSMutableArray *wishlist
@end

@implementation WishlistViewController

// @synthesize line is redundant since 2012

- (void)viewDidLoad {
    ...

(在我在下面引用的你的代码中,我假设你已经这样做了,我将 self-> 替换为 self.,因为前者会伤害我的大脑来打字甚至查看。)

现在,当他提到分配wishlistviewDidLoad 行时,Carl 确实是正确的。很明显,您可能有一些误解,因为您正在创建一个数组,然后在 2 行后替换它:

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]];

但是您也可以删除第一行 wishlist = 并像 Carl 建议的那样制作方法结果的可变副本,但是像 self.wishlist = [wliDAO getWishlistItemsWithWishlistId:wlDAO.id].mutableCopy; 那样做的话就不那么罗嗦了

可能发生的事情是wishlistDAO 正在返回其内部可变数组,它可能会在稍后使用和更改,这就是wishlist 设置的内容,即。设置为参考,即。指向wishlistDAO 自己的NSMutableArray 对象的相同指针值。这很糟糕,这是上面建议的更改所修复的。然后在您稍后再次调用相同的方法:

[wishlist addObject:[[wliDAO getWishlistItemsWithWishlistId:wlDAO.id] lastObject]];

我猜它首先会擦除它的数组,这会擦除你的数组,因为它是同一个对象。也许它也会释放它并在此过程中创建一个新的,留下 wishlist 作为现在空数组的唯一保留。

为了安全起见,最好采用最佳做法:

  1. getWishlistItems 应该返回一个新的数组副本,而不是对某个内部属性的引用,或者以任何其他方式返回后可能会更改的引用。该方法的结果应声明为不可变的NSArray
  2. 如上所述,您的视图控制器应在该方法的结果上使用addObjectsFromArray,或将wishlist 分配给它的mutableCopy

单独做任何一个都可以解决您的问题。这两个错误肯定是原因的结合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-04
    • 2021-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-12
    • 2011-02-07
    相关资源
    最近更新 更多