您的位置:首页 > 移动开发 > IOS开发

iOS指南系列:程序运行非我所设想:tableview

2012-04-14 23:33 429 查看


这个题目实在吧:)我们接上篇讲,fix那么多问题后,出来的table居然是没有内容的!


Getting Started: When What’s Supposed to Happen, Doesn’t

- (NSString *)tableView:(UITableView *)tableView
titleForHeaderInSection:(NSInteger)section{
return @"States";
}
- (NSString *)tableView:(UITableView *)tableView
titleForFooterInSection:(NSInteger)section {
return @"USA";
}  //可以用来显示一些当前更新时间,网络连接状态等信息

using 
insertRowsAtIndexPaths:withRowAnimation:
and
deleteRowsAtIndexPaths: withRowAnimation:

[/code]

我们完成之前的步骤后,经过大量的调试工作后应用程序运行没有崩溃。但它显示了出人意料的空表,像这样:





当你期待一些事情发生,但它没有发生,这类问题一般比较困难,它没有错误提示的!但也有一些技巧,你可以使用来解决。先来看看本教程将使用NSLog()/或者断点来处理这个问题。

为表视图控制器类是listViewController的 Segue identity进行后,应用程序应该载入ListViewController,并显示在屏幕上的view。确保视图控制器的方法实际是被调用到了是可以测试这个假设的。一般而言我们会在 viewDidLoad中,似乎像是一个很好的地方。

ListViewController.m,
增加 NSLog() 在函数 viewDidLoad 中:

- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(@"viewDidLoad is called");
}

当您运行的应用程序,你应该期望看到的文本“viewDidLoad中被称为”在调试窗格后,按tap
me按钮。尝试一下。调试信息没有出现在Debug窗格。这意味着在ListViewController类没有被使用到,否则肯定会进入到viewdidload!

这通常意味着,你可能忘了告诉storyboard要使用该表的视图控制器ListViewController类;也许就直接使用了默认的tableviewcontroller的控制器。





Yep, the Class field in the Identity Inspector is set to the default value, UITableViewController. Change it to ListViewController and run the app again. Now the “viewDidLoad is called” text will appear
in the Debug output:

没错,在身份检查的类字段发现其设置为默认值,UITableViewController。改变它为ListViewController,并再次运行该应用程序。现在“viewDidLoad中添加的”文字会出现在调试窗口中,但是!

Problems[18375:f803] You tapped on: <UIRoundedRectButton: 0x6894800; frame = (119 189; 82 37);
opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x68948f0>>
Problems[18375:f803] viewDidLoad is called

程序又奔溃了,似乎出现了新的问题:

Note: Whenever
your code doesn’t appear to do anything, place a few NSLog() statements
in strategic places, to see whether certain methods are actually being called and which path the CPU takes through these methods. Use NSLog() to test your assumptions about what the code does.

关于断言:

Assertion Failures

新的crash有点意思,是 SIGABRT 类型, Debug 输出窗口显示内容:

Problems[18375:f803] *** Assertion failure in -[UITableView _createPreparedCellForGlobalRow:
withIndexPath:], /SourceCache/UIKit_Sim/UIKit-1912.3/UITableView.m:6072

我们有一个“断言失败”,关于一个UITableView的preparedcellforglobalrow(共用row?)。断言是一个内部的一致性检查,什么是错的时候抛出异常。你可以在自己的代码中增加断言,例如:

- (void)doSomethingWithAString:(NSString *)theString
{
NSAssert(string != nil, @"String cannot be nil");
NSAssert([string length] >= 3, @"String is too short");
. . .
}

我们上面的方法需要一个NSString对象作为其参数,但代码不会允许呼叫者传递零或有少于三个字符的字符串。如果这些条件得不到满足,应用程序将中止于异常。

您可以使用断言作为一个防御性编程技术,使你总是确保代码的行为预期。通常只在调试版本中断言才会启用,所以他们没有最终发布在App
Store上的应用程序中,不会影响运行的。

在这个例子中,uitableviw的断言出发了问题,你不能确性在什么地方,程序最后因为 内存访问问题,停在main,同时call stack也没有具体的内容帮助我们

按照之前的说法,我们尽力寻找接近的我们能理解的函数,例如其中的layoutSubviews 以及_updateVisibleCellsNow:.
看起来和cell有关





Continue running the app to see if you’re going to get a better error message – remember, you’re currently paused just before the exception will be thrown. Press the Continue Program Execution button, or type the following into the Debug Pane:

继续运行应用程序,看看你会得到更多的错误消息 - 要记得,你目前已暂停的位置在引发二次异常(导致崩溃的)之前。按继续执行程序“按钮,或进入调试窗格中键入以下内容:

(lldb) c

现在,debug窗口输出的内容更明确了:

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException',
reason: 'UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:'
*** First throw call stack:
(0x13ba052 0x154bd0a 0x1362a78 0x99a2db 0xaaee3 0xab589 0x96dfd 0xa5851 0x50301
0x13bbe72 0x1d6492d 0x1d6e827 0x1cf4fa7 0x1cf6ea6 0x1cf6580 0x138e9ce 0x1325670
0x12f14f6 0x12f0db4 0x12f0ccb 0x12a3879 0x12a393e 0x11a9b 0x2722 0x2695)
terminate called throwing an exception

所有的权利,这是一个很好的提示。显然的UITableView数据源没有返回一个有效的cell 从函数tableView:cellForRowAtIndexPath:因此,添加一些调试输出到该方法inListViewController.m的如下:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

NSLog(@"the cell is %@", cell);

cell.textLabel.text = [list objectAtIndex:indexPath.row];

return cell;
}


你要是了解tabledatasource,肯定能想起来少了什么代码。。。

Problems[18420:f803] the cell is (null)

OK, so that means the call to dequeueReusableCellWithIdentifier: returned
nil, something that only happens when the cell with the identifier “Cell” could not be found (because the app uses a storyboard with prototype cells).

假如不是可视化定义cell,一般的代码如下:

UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentifier:@"Cell"];

if (cell == nil)

cell = [[UITableViewCellalloc]
initWithStyle:UITableViewCellStyleSubtitlereuseIdentifier:@"Cell"];

当然,这是一个愚蠢的错误,你毫无疑问应该在很长一段时间前解决了它,因为Xcode中对此已警告通过方便的编译器警告:“原型细胞必须有重用标识符。”我告诉你不要忽略这些警告! :P





打开storyboard, 选中cell (the single cell at the top of the table view that says “Title”,要注意的是,在缩略图下,无法选中的,先放大), 设置 identifier 为 Cell:





这个修改完成后,程序告警全消失了,运行程序,debug输出如下:但是还是没有内容可见

Problems[7880:f803] the cell is <UITableViewCell: 0x6a6d120; frame = (0 0; 320 44); text = 'Title'; layer = <CALayer: 0x6a6d240>>
Problems[7880:f803] the cell is <UITableViewCell: 0x6877620; frame = (0 0; 320 44); text = 'Title'; layer = <CALayer: 0x6867140>>
Problems[7880:f803] the cell is <UITableViewCell: 0x6da1e80; frame = (0 0; 320 44); text = 'Title'; layer = <CALayer: 0x6d9fae0>>
Problems[7880:f803] the cell is <UITableViewCell: 0x6878c40; frame = (0 0; 320 44); text = 'Title'; layer = <CALayer: 0x6878f60>>
Problems[7880:f803] the cell is <UITableViewCell: 0x6da10c0; frame = (0 0; 320 44); text = 'Title'; layer = <CALayer: 0x6d9f240>>
Problems[7880:f803] the cell is <UITableViewCell: 0x6879640; frame = (0 0; 320 44); text = 'Title'; layer = <CALayer: 0x6878380>>


Verify Your Assumptions

你通过NSLog显示,6个表单元被创建,但仍然没有在表中可见。怎么办?好吧,如果你点选位模拟器,你会发现,现在实际上可以选择在表视图上半个cell。显然,cell是有,但他们只是空白:





Time for some more debug logging. Change your previous NSLog() statement to:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

cell.textLabel.text = [list objectAtIndex:indexPath.row];

NSLog(@"the text is %@", [list objectAtIndex:indexPath.row]);

return cell;
}


现在你记录你的数据模型的内容。运行程序,看看它说的:

Problems[7914:f803] the text is (null)
Problems[7914:f803] the text is (null)
Problems[7914:f803] the text is (null)
Problems[7914:f803] the text is (null)
Problems[7914:f803] the text is (null)
Problems[7914:f803] the text is (null)

That explains why nothing shows up in the cells: because the text is always nil. However, if you check the code, at the top of the class in initWithStyle: you’re
definitely putting strings into the list array:

[list addObject:@"One"];
[list addObject:@"Two"];
[list addObject:@"Three"];
[list addObject:@"Four"];
[list addObject:@"Five"];

在这样的情况下,它总是一个好主意,再次测试你的假设。也许你应该看看究竟是你的阵列内。改变以往的NSLog() tableView:cellForRowAtIndexPath: 修改为:

NSLog(@"array contents: %@", list);

debug 窗口的输出:

Problems[7942:f803] array contents: (null)
Problems[7942:f803] array contents: (null)
Problems[7942:f803] array contents: (null)
Problems[7942:f803] array contents: (null)
Problems[7942:f803] array contents: (null)
Problems[7942:f803] array contents: (null)

啊哈!一个灯泡熄灭在你的脑袋。这是从来不会工作得,因为有人忘了在一开始就实际分配数组对象。 “list”总是空的,所以调用addObject:andobjectAtIndex:从来没有任何影响。

你应该分配列表中的对象,当您的视图控制器加载内initWithStyle,似乎是一个不错的地方。改变这种状况的方法:
list = [NSMutableArray arrayWithCapacity:10];

- (id)initWithStyle:(UITableViewStyle)style
{
if (self == [super initWithStyle:style])
{
list = [NSMutableArray arrayWithCapacity:10];

[list addObject:@"One"];
[list addObject:@"Two"];
[list addObject:@"Three"];
[list addObject:@"Four"];
[list addObject:@"Five"];
}
return self;
}


[/code]
给一个尝试。哎呀,仍然一无所获!调试输出再次说:

Problems[7971:f803] array contents: (null)
. . . and so on . . .

这种事情是很令人沮丧,但请记住,你最终会得到一个结论,如果您已经验证所有假设。因此,现在问的问题是,initWithStyle:实际上在何时被调用?

=======》

如果你设置断点,也可以清楚地看到initwithsyle没有被调用到,理由是 :

As you may have expected, no such thing happens. initWithStyle: 一般在代码初始化viewctroller用到,而目前是
从nib加载的, initWithCoder:会被调用到。







运行程序,还是crash这次绝对是memory问题了:

cell.textLabel.text = [listobjectAtIndex:indexPath.row];



总结:

1.要了解lifecycle,包括events

2.nslog能帮助验证,但解决问题靠大脑 和知识
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐