UITableView在键值观察器仍与其注册时被释放。

4

我使用 MFSideMenu 控制器来实现大多数具有侧边菜单功能的应用程序。所有应用程序在 iOS 7 及之前版本上都能正常工作,但在运行 iOS 8 的 iPhone 6 设备上则不行(在运行 iOS 8 的 iPhone 5 上可以正常工作)。

我似乎无法弄清楚该怎么做才能解决我正在收到的这个错误。当点击左侧菜单 UITableView 上的单元格时,应用程序会崩溃并显示此错误。

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x7fc492877e00 of class UITableView was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x7fc492634860>

注意:UITableView是一个XIB IBOutlet。
以下是代码示例。
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.

    // This will remove extra space on top of the tableview
    self.automaticallyAdjustsScrollViewInsets = NO;
    // This will remove extra separators from tableview
    self.tblViewMenu.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];

    // menu titles
    arrMenu = [Config returnArrLeftMenu];

    // check login status
    [self checkLoginStatus];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CustomCellIdentifier = @"CustomCellIdentifier ";

MenuCell *mCell = (MenuCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier];

// menu cell
if (mCell == nil) {
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"MenuCell" owner:self options:nil];

    for (id oneObject in nib) if ([oneObject isKindOfClass:[MenuCell class]])
        mCell = (MenuCell *)oneObject;

}

    mCell.selectionStyle = UITableViewCellSelectionStyleBlue;
    mCell.contentView.backgroundColor = [UIColor clearColor];
    mCell.backgroundColor = [UIColor clearColor];
    mCell.lblTitle.text = [arrMenu objectAtIndex:indexPath.row];

    return mCell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

    // load selected menu view on to the MAinViewController
    MainViewController *mainController = [[MainViewController alloc] initWithNibName:@"MainViewController" bundle:nil];

    mainController.title = [arrMenu objectAtIndex:indexPath.row];
    mainController.menuIndex = (int)indexPath.row;

    UINavigationController *navigationController = self.menuContainerViewController.centerViewController;
    NSArray *controllers = [NSArray arrayWithObject:mainController];
    navigationController.viewControllers = controllers;
    [self.menuContainerViewController setMenuState:MFSideMenuStateClosed];

}

1
你知道这个错误信息的含义,对吧? - matt
你在使用UITableView的时候有用到KVO吗? - Eyeball
1
需要注意的一件事情是,你的CustomCellIdentifier末尾有一个空格,这可能不是错误的原因。 - Eyeball
是的。我不小心删掉了空格,现在它可以工作了 :) 非常感谢您的指导。您能告诉我这样做的原因吗?我以前没有遇到过这样的异常。 - smartsanja
1
你可以在@property (nonatomic, strong) IBOutlet UITableView *tableView;中尝试使用strong属性。 - Sergey Petruk
显示剩余4条评论
3个回答

12

您可能正在观察类似以下代码的TableView属性更改:

[self.tblViewMenu addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]

观察者(您的类实例)的引用使用不安全的未保留语义存储。您将不得不在dealloc()期间清理它,以避免应用程序崩溃(ARC无法为您处理此问题)。

因此,您的dealloc()应该像这样取消注册观察者:

- (void)dealloc {
    [self.tblViewMenu removeObserver:self forKeyPath:@"frame"];
}

你需要将“frame”替换为你感兴趣的属性的名称。

一定要在适当的Apple指南中检查键值观察(KVO)的基本概念。

编辑

我刚刚检查了MFSideMenu的源代码。在那段代码中没有使用KVO,因此崩溃很可能不是由该组件引起的。


5
对于Swift版本,您可以使用“deinit”方法来移除所有的观察者。
 deinit {
    self.yourtable.removeObserver(self, forKeyPath: "keyname")
}

0
你确定在控制器被释放前,你的tableView已经正确清除了吗?试着给你的控制器添加一个-(void) dealloc{}方法,并在其内部设置一个断点。如果当dealloc被调用时,你的控制器仍然持有对tableView的引用,那么就会导致类似于你所看到的问题。
如果确实是这种情况,那么你需要重写控制器的removeFromParentViewController方法,并手动将tableView属性设置为nil。例如:
-(void) removeFromParentViewController {
    [super removeFromParentViewController];
    if(self.tableView!=nil) {
        self.tableView.delegate = nil;
        self.tableView.dataSource = nil;
        self.tableView = nil;
    }
}

希望这能有所帮助!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接