0

我发现当一个对象没有明确地处理它们时,一些 Obj-C 消息没有得到正确转发。

这是一个例子:

创建一个新的 UITableView,如:

UITableView *v ? [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 0, 0) style:UITableViewStylePlain];

然后使用以下方法创建一个 NSObject 的实例:

(A) - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSLog(@"tableView:numberOfRowsInSection:");
    return 1;
}
(B) - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"tableView:cellForRowAtIndexPath:");
    return nil;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    NSLog(@"methodSignatureForSelector: %@", NSStringFromSelector(aSelector));
    return [NSMethodSignature signatureWithObjCTypes:"@@:@"];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"forwardInvocation: %@", NSStringFromSelector(anInvocation.selector));
}

让我们将此对象称为 ds 并将其设置为表格视图的数据源:

v.dataSource = ds;

当您运行代码时,您应该看到方法 (A) 和 (B) 都被表视图以预期的顺序调用:

LOG: tableView:numberOfRowsInSection:
LOG: tableView:cellForRowAtIndexPath:

现在它会变得有趣,尝试删除(或只是更改名称)方法(A),你应该得到:

LOG: methodSignatureForSelector: tableView:numberOfRowsInSection:
LOG: forwardInvocation: tableView:numberOfRowsInSection:

这很好,因为表视图尝试调用tableView:numberOfRowsInSection:并且由于该方法不存在,消息被转发到forwardInvocation:

显然,这是所有引用未在对象中定义的方法的消息的默认行为。

但是尝试恢复(A)方法并删除(B),你应该得到:

LOG: tableView:numberOfRowsInSection:

...和一个错误告诉你tableView:cellForRowAtIndexPath:被调用但没有返回一个单元格。

由于某种原因tableView:cellForRowAtIndexPath:被调用(或不被调用)但没有遵循预期的forwardInvocation:路径。

这是一个错误吗?

难道我做错了什么?

是否有一些明确不转发的消息?


即使respondsToSelector:应该向这两个函数返回 FALSE,但在实现中的某处将cellForRowAtIndexPath:的消息发送到其他对象并破坏默认的预期行为......

我学到的基本上是:

如果您计划响应对象中的消息,即使通过forwardInvocation:的某些实现,您也应该始终重载respondsToSelector:方法并为您计划在对象中实现的所有消息返回 TRUE。

4

2 回答 2

2

以下是我基于一些实验的假设:

cellForRowAtIndexPath不同之处在于,如果未定义表视图,则不会尝试调用,而会尝试调用numberOfRowsInSection. 因此,您只能看到带有 的转发机制numberOfRowsInSection。告诉您未返回单元格的错误只是措辞不佳且具有误导性。

我通过观察得出这个结论,如果数据源没有定义,则会调用cellForRowAtIndexPathon 上的实现。UITableViewController因此,表格视图显然正在检查是否存在。因此,有理由认为发生的事情是这样的:

  1. dataSource如果定义则调用
  2. 否则,UITableViewController如果定义则调用
  3. 否则,不要调用任何东西

如果达到 (3),则在下游某处抛出错误,因为表格视图的内部单元格变量是nil.

于 2013-11-05T21:00:13.160 回答
0

仅当运行时系统无法在目标类或其任何祖先中找到给定选择器的匹配项时,它才会转发消息。但是,如果运行时系统对选择器的搜索成功,它就会直接调用方法的实现——无需转发。

顺便说一句,如果查找失败,则运行时系统forwardingTargetForSelector:首先调用;forwardInvocation:然后它仅在forwardingTargetForSelector:返回nilor时调用self

于 2013-11-05T20:42:28.213 回答