邮件转发不适用于特定邮件

时间:2013-11-05 20:23:58

标签: ios objective-c cocoa

我发现当某个对象没有明确处理它们时,某些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并将其设置为表视图的dataSource:

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。

2 个答案:

答案 0 :(得分:2)

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

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

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

  1. 如果已定义
  2. ,请致电dataSource
  3. 否则,如果已定义
  4. ,请致电UITableViewController
  5. 否则,不要打电话
  6. 如果达到(3),则抛出错误的下游某处,因为表视图的内部单元格变量为nil

答案 1 :(得分:0)

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

顺便说一句,如果查找失败,运行时系统首先调用forwardingTargetForSelector:;只有当forwardInvocation:返回forwardingTargetForSelector:nil时,它才会调用self