EXC_BAD_ACCESS in heightForRowAtIndexPath iOS

问题描述:

我正在研究一个应用程序,其中有一个UITableViewCell的自定义子类。我想根据里面的文本使单元格的高度动态化。我尝试在我的heightForRowAtIndexPath方法中做到这一点。但是我遇到了一些问题,下面的代码会导致EXC_BAD_ACCESS(code = 2 address = 0xb7ffffcc)错误。EXC_BAD_ACCESS in heightForRowAtIndexPath iOS

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 

    PostCell *cell = (PostCell *)[tableView cellForRowAtIndexPath:indexPath]; 

    CGSize postTextSize; 
    if (![cell.postText.text isEqualToString:@""]) { 
     postTextSize = [cell.postText.text sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:CGSizeMake(cell.postText.frame.size.width, 200)]; 
    } else { 
     postTextSize = CGSizeMake(cell.postText.frame.size.width, 40); 
    } 

    return postTextSize.height + cell.userName.frame.size.height + 10; 
} 

如果我不是有:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    return 100; 
} 

它的工作原理。

它似乎在这条线上崩溃:PostCell *cell = (PostCell *)[tableView cellForRowAtIndexPath:indexPath];,我不知道为什么。我试图建立清洁和重置模拟器,但都没有工作。任何想法为什么发生这种情况?

我试图重现这个问题。事实证明,在heightForRowAtIndexPath内调用cellForRowAtIndexPath:会导致heightForRowAtIndexPath被递归调用。这是3递归步骤后堆栈回溯的摘录:

frame #0: 0x000042d0 DocInteraction`-[DITableViewController tableView:heightForRowAtIndexPath:] + 48 at DITableViewController.m:262 
frame #1: 0x0054f688 UIKit`-[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3437 
frame #2: 0x0055040f UIKit`-[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 144 
frame #3: 0x00551889 UIKit`-[UITableViewRowData numberOfRows] + 137 
frame #4: 0x00553dac UIKit`-[UITableViewRowData globalRowsInRect:] + 42 
frame #5: 0x003f82eb UIKit`-[UITableView(_UITableViewPrivate) _visibleGlobalRowsInRect:] + 177 
frame #6: 0x004001e6 UIKit`-[UITableView cellForRowAtIndexPath:] + 113 
frame #7: 0x000042f2 DocInteraction`-[DITableViewController tableView:heightForRowAtIndexPath:] + 82 at DITableViewController.m:262 
frame #8: 0x0054f688 UIKit`-[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3437 
frame #9: 0x0055040f UIKit`-[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 144 
frame #10: 0x00551889 UIKit`-[UITableViewRowData numberOfRows] + 137 
frame #11: 0x00553dac UIKit`-[UITableViewRowData globalRowsInRect:] + 42 
frame #12: 0x003f82eb UIKit`-[UITableView(_UITableViewPrivate) _visibleGlobalRowsInRect:] + 177 
frame #13: 0x004001e6 UIKit`-[UITableView cellForRowAtIndexPath:] + 113 
frame #14: 0x000042f2 DocInteraction`-[DITableViewController tableView:heightForRowAtIndexPath:] + 82 at DITableViewController.m:262 
frame #15: 0x0054f688 UIKit`-[UISectionRowData refreshWithSection:tableView:tableViewRowData:] + 3437 
frame #16: 0x0055040f UIKit`-[UITableViewRowData(UITableViewRowDataPrivate) _ensureSectionOffsetIsValidForSection:] + 144 
frame #17: 0x00551889 UIKit`-[UITableViewRowData numberOfRows] + 137 
frame #18: 0x003ff66d UIKit`-[UITableView noteNumberOfRowsChanged] + 119 
frame #19: 0x003ff167 UIKit`-[UITableView reloadData] + 764 

最后程序崩溃。在我的模拟器上,当后面的轨迹深度大约为57000时,会发生这种情况。


老答案(没有错,但并不能说明 EXC_BAD_ACCESS):

的问题是,

[tableView cellForRowAtIndexPath:indexPath] 

回报nil对于当前不可见的行。表视图仅分配显示当前可见行所需的很多单元。当您滚动表格视图时,单元格将被重新使用。

但是heightForRowAtIndexPath被调用为全部显示任何行之前的表视图的单元格。

因此,您不应该从表格视图单元格中获取文本来计算heightForRowAtIndexPath中的高度。您应该从数据源中获取文本。

+0

通过什么机制返回nil导致EXC_BAD_ACCESS? – grahamparks

+1

@grahamparks:好问题!我试图重现该问题,并将信息添加到我的答案中。 –

+0

谢谢,使用数据源而不是工作。 – Anders

tableView heightForRowAtIndexPathcellForRowAtIndexPath之前调用,在显示单元格之前,需要先计算高度。

你应该从你的数据源获取text,而不是从cell

我有一个类似的问题,碰上了,只是和大家分享我现在做的这对我来说

CGFloat mycell_height; 

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"MyCell"]; 
    mycell_height = cell.frame.size.height; 
} 

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    return mycell_height; //assuming all cells are the same 
} 

希望这个作品很好帮助任何寻找这个和新的iOS编程像我这样的人:-)

在这种情况下,你应该调用UITableViewDataSource方法获取单元格的indexPath,而不是cellForRowAtIndexPath:UITableView的方法bec在使用tableView:heightForRowAtIndexPath:第一次调用tableView时没有任何单元格的时候。

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath: (NSIndexPath *)indexPath { 
    PostCell *cell = (PostCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath]; 

    CGSize postTextSize; 
    if (![cell.postText.text isEqualToString:@""]) { 
    postTextSize = [cell.postText.text sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:CGSizeMake(cell.postText.frame.size.width, 200)]; 
    } else { 
     postTextSize = CGSizeMake(cell.postText.frame.size.width, 40); 
    } 

    return postTextSize.height + cell.userName.frame.size.height + 10; 
} 

它的工作原理,并没有造成EXC_BAD_ACCESS例外。

+0

它适用于我,但它意味着UITableViewDataSource方法在单元格在tableview中创建之前工作,对吗? @ninjaproger –

SWIFT:

使用该知道什么时候该表已加载:

extension UITableView { 
    func reloadData(completion:()->()) { 
     UIView.animateWithDuration(0, animations: { self.reloadData() }) 
      { _ in completion() } 
    } 
} 

在viewDidLoad中创建一个布尔值

var tables_loaded = Bool(false) 

然后:

tableView.reloadData { 
    tables_loaded = true 
    // call functions or other stuff regarding table manipulation. 
    // tableView.beginUpdates() 
    // tableView.endUpdates() 
} 

然后在

override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { 
      if tables_loaded { 
       let cell = tableView.cellForRowAtIndexPath(indexPath) 
       // Do your magic here! 
      } 

} 
+0

不相关,但'Bool(false)'似乎没用:'false'是正确的,Swift可以推断它的类型。我对你为什么要用'Bool'初始化设置'var'感兴趣? – vrwim

+1

@vrwim因为我是Swift的noob。 =) – Philip